跟踪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();
}
}
您可以通过响应式扩展并将Observable.FromEventPattern
与Observable.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))来计算是否所有内容都已完成。
这不是最正确的做法。但阅读您的场景,这可能会完成工作。
如果您将装载货物、集装箱和包裹作为异步任务,那么您有几个选项。正如其他人建议的那样,您可以使用WhenAll
或Join()
等待所有线程完成后再继续。但是,如果您的线程必须保持活动状态,并且线程在完成加载后不会停止,则可以像下面这样使用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;
}