【问题标题】:(A)Wait for a number of instances to raise an Event exactly once(A) 等待多个实例恰好引发一次事件
【发布时间】:2016-09-08 21:18:05
【问题描述】:

我有一个列表,这个 SomeAttachmentClass 有一个 UploadCompleted 事件,即在调用 .Save() 方法时,只引发一次。 .Save() 方法在保存和上传实际完成之前不会阻塞,而是立即返回并通过所述事件发出完成信号。

现在我想要并且需要做的基本上是等待 List 中的所有实例(确切地)引发该事件一次,然后才继续。问题是 - 我将如何最简单地做到这一点/优雅吗?

我正在考虑在每个实例上将 RX 的 FromEvent(..) 与 .Take(1) 结合使用,在所有这些每个实例流上执行 .SelectMany() 并等待......但我不确定是否有更好的出路。

【问题讨论】:

  • 您可以复制列表,并在每个触发的事件上从复制列表中删除引发事件的元素。当列表为空时 - 你会知道所有项目都引发了事件

标签: c# events task-parallel-library system.reactive


【解决方案1】:

这可以解决问题 - 感谢 Shlomo 的起点。

void Main()
{

    Func<SomeAttachment, IObservable<object>> save = sa =>
        Observable.Create<object>(o =>
        {
            var ob =
                Observable
                    .FromEventPattern<object>(
                        eh => sa.UploadCompleted += eh,
                        eh => sa.UploadCompleted -= eh)
                    .Take(1)
                    .Select(x => x.EventArgs);
            var subscription = ob.Subscribe(o);
            sa.Save();
            return subscription;
        });

    var list = Enumerable.Range(0, 10).Select(i => new SomeAttachment(i)).ToList();

    list
        .ToObservable()
        .SelectMany(o => save(o))
        .ToArray()
        .Subscribe(_ => Console.WriteLine("All Complete. Handling logic goes here."));
}

public class SomeAttachment
{
    private static Random random = new Random();
    private readonly int _id;
    public SomeAttachment(int id)
    {
        _id = id;
    }
    public int Id
    {
        get { return _id; }
    }

    public async Task Save()
    {
        //await Task.Delay(TimeSpan.FromMilliseconds(random.Next(1000, 3000)));
        UploadCompleted?.Invoke(this, new object());
    }

    public event EventHandler<object> UploadCompleted;
}

【讨论】:

    【解决方案2】:

    这是一个 Rx 实现。您无需等待,Rx 会为您完成。注释掉的行用于在 LinqPad 中调试/运行:

    void Main()
    {
        var list = Enumerable.Range(0, 10).Select(i => new SomeAttachment(i)).ToList();
    
        list.ToObservable()
            .Do(sa => sa.Save())
            //.Do(sa => Console.WriteLine($"{sa.Id}: Save called"))
            .Select(sa => Observable.FromEventPattern<object>(eh => sa.UploadCompleted += eh, eh => sa.UploadCompleted -= eh))
            .SelectMany(o => o.Take(1))
            //.Do(o => Console.WriteLine($"{(o.Sender as SomeAttachment).Id}: Upload completed."))
            .All(_ => true)
            .Take(1)
            .Subscribe(_ => Console.WriteLine("All Complete. Handling logic goes here."));
    }
    
    // Define other methods and classes here
    
    public class SomeAttachment
    {
        private static Random random = new Random();
        private readonly int _id;
        public SomeAttachment(int id)
        {
            _id = id;
        }
        public int Id
        {
            get { return _id; }
        }
    
        public async Task Save()
        {
    
            await Task.Delay(TimeSpan.FromMilliseconds(random.Next(1000, 3000)));
            UploadCompleted?.Invoke(this, new object());
        }
    
        public event EventHandler<object> UploadCompleted;
    
    }
    

    【讨论】:

    • 如果 .Save() 触发 UploadCompleted 附加到,则您有竞争条件。我认为您需要在.Save() 之前附加。
    • 是的,如果您取出await Task.Delay,则不会显示"All Complete..." 消息。
    猜你喜欢
    • 1970-01-01
    • 2012-11-14
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    • 2011-07-21
    • 1970-01-01
    • 2012-09-09
    • 2021-03-17
    相关资源
    最近更新 更多