跟踪x个事件何时触发

本文关键字:何时触 事件 跟踪 | 更新日期: 2023-09-27 18:12:25

通常这是我会花几个小时浏览谷歌和stackoverflow的东西,但是我遇到了一个问题,我该如何为搜索引擎写这个词。

我希望有一个简单的方法来实现这一点,因为我目前的方法感觉太"hackish"了

我需要做的是,如果跟踪几个数据源何时完成加载,只有当所有数据源都完成加载时,我才能加载一个新视图(这是WPF mvvm)。现在数据通过一个称为Repository的静态类加载,每个类都创建一个线程,并确保它们一次只能发生一个加载操作(以避免多个线程试图加载到同一个集合),当它们完成加载时,每个类都会触发一个名为LoadingCompleted的事件。

我有一个加载大部分数据的单一位置(第一次,有其他位置的数据被重新加载),我计划的是挂钩到每个存储库的OnLoaded事件,并跟踪哪些已经返回,当一个返回时,将其标记为已加载并检查是否有剩余。如果没有,则加载新视图,否则什么都不做。

像这样:

ShipmentRepository.LoadingComplete += ShipmentRepository_LoadingComplete;
ContainerRepository.LoadingComplete += ContainerRepository_LoadingComplete;
void ContainerRepository_LoadingComplete(object sender, EventArgs e)
{
           _containerLoaded = true;
           CheckLoaded();
}

void ShipmentRepository_LoadingComplete(object sender, EventArgs e)
{
           _shipmentsLoaded = true;
           CheckLoaded();
}
private void CheckLoaded()
{
    if(_shipmentsLoaded && _containersLoaded && _packagesLoaded)
    {
        LoadView();
    }
}

跟踪x个事件何时触发

您可以通过响应式扩展并将Observable.FromEventPatternObservable.Zip方法结合使用来实现这一点。您应该能够执行如下操作:

var shipmentRepositoryLoadingComplete = Observable.FromEventPattern<EventHandler,EventArgs>(h => ShipmentRepository.LoadingComplete += h, h => ShipmentRepository.LoadingComplete -= h);
var containerRepositoryLoadingComplete = Observable.FromEventPattern<EventHandler, EventArgs>(h => ContainerRepository.LoadingComplete += h, h => ContainerRepository.LoadingComplete -= h);

然后像这样订阅可观测的数据:

 var subscription = Observable.Zip(shipmentRepositoryLoadingComplete, containerRepositoryLoadingComplete)
            .Subscribe(l => LoadView()));

订阅需要保持活动状态,因此可以将其保存为私有变量。当调用两个完整事件时,应该调用LoadView方法。下面是我用来测试此方法的工作控制台示例。

using System;
using System.Reactive.Linq;
namespace RxEventCombine
{
    class Program
    {
        public event EventHandler event1;
        public event EventHandler event2;
        public event EventHandler event3;
        public Program()
        {
            event1 += Event1Completed;
            event2 += Event2Completed;
            event3 += Event3Completed;
        }      
        public void Event1Completed(object sender, EventArgs args)
        {
            Console.WriteLine("Event 1 completed");
        }
        public void Event2Completed(object sender, EventArgs args)
        {
            Console.WriteLine("Event 2 completed");
        }
        public void Event3Completed(object sender, EventArgs args)
        {
            Console.WriteLine("Event 3 completed");
        }
        static void Main(string[] args)
        {
            var program = new Program();
            var event1Observable = Observable.FromEventPattern<EventHandler,EventArgs>(h => program.event1 += h, h => program.event1 -= h);
            var event2Observable = Observable.FromEventPattern<EventHandler, EventArgs>(h => program.event2 += h, h => program.event2 -= h);
            var event3Observable = Observable.FromEventPattern<EventHandler, EventArgs>(h => program.event3 += h, h => program.event3 -= h);
            using (var subscription = Observable.Zip(event1Observable, event2Observable, event3Observable)
                .Subscribe(l => Console.WriteLine("All events completed")))
            {
                Console.WriteLine("Invoke event 1");
                program.event1.Invoke(null, null);
                Console.WriteLine("Invoke event 2");
                program.event2.Invoke(null, null);
                Console.WriteLine("Invoke event 3");
                program.event3.Invoke(null, null);
            }
            Console.ReadKey();
        }
    }
}

Invoke event 1
Event 1 completed
Invoke event 2
Event 2 completed
Invoke event 3
Event 3 completed
All events completed

另一种方法是:添加LoadingCompleted属性。对于每个启动线程的实例,将该对象返回到列表中。在每次loadcompleted时,将该属性设置为true,并在通过列表(myList)捕获loadcompleted循环的地方。任何(x=>LoadingCompleted == false))来计算是否所有内容都已完成。

这不是最正确的做法。但阅读您的场景,这可能会完成工作。

如果您将装载货物、集装箱和包裹作为异步任务,那么您有几个选项。正如其他人建议的那样,您可以使用WhenAllJoin()等待所有线程完成后再继续。但是,如果您的线程必须保持活动状态,并且线程在完成加载后不会停止,则可以像下面这样使用System.Threading.CountdownEvent:

编辑

添加了如何设置线程和处理事件。还将示例从静态Program移动到实例,更接近于您的情况。同样,如果在线程加载数据后不需要在线程中执行任何操作,只需完全跳过CountdownEvent并等待所有线程完成。更简单,不需要事件,可以使用Join()或本例中的Task.WaitAll()来实现。

class Program
{
    static void Main(string[] args)
    {
        var myWpfObject = new MyWpfObject();
    }        
}
public class MyWpfObject
{
    CountdownEvent countdownEvent;
    public MyWpfObject()
    {
        ShipmentRepository ShipmentRepository = new ShipmentRepository();
        ContainerRepository ContainerRepository = new ContainerRepository();
        PackageRepository PackageRepository = new PackageRepository();
        ShipmentRepository.LoadingComplete += Repository_LoadingComplete;
        ContainerRepository.LoadingComplete += Repository_LoadingComplete;
        PackageRepository.LoadingComplete += Repository_LoadingComplete;
        Task[] loadingTasks = new Task[] {
            new Task(ShipmentRepository.Load),
            new Task(ContainerRepository.Load),
            new Task(PackageRepository.Load)
        };
        countdownEvent = new CountdownEvent(loadingTasks.Length);
        foreach (var task in loadingTasks)
            task.Start();
        // Wait till everything is loaded.
        countdownEvent.Wait();
        Console.WriteLine("Everything Loaded");
        //Wait till aditional tasks are completed.
        Task.WaitAll(loadingTasks);
        Console.WriteLine("Everything Completed");
        Console.ReadKey();
    }
    public void Repository_LoadingComplete(object sender, EventArgs e)
    {
        countdownEvent.Signal();
    }
}

和一个mock Repository类:

public class ShipmentRepository
{
    public ShipmentRepository()
    {
    }
    public void Load()
    {
        //Simulate work
        Thread.Sleep(1000);
        if (LoadingComplete != null)
            LoadingComplete(this, new EventArgs());
        Console.WriteLine("Finished loading shipments");
        DoAditionalWork();
    }
    private void DoAditionalWork()
    {
        //Do aditional work after everything is loaded
        Thread.Sleep(5000);
        Console.WriteLine("Finished aditional shipment work");
    }
    public event EventHandler LoadingComplete;
}