上升的事件没有阻塞并以正确的顺序接收事件

Sin*_*atr 4 c# events multithreading asynchronous

这需要先解释一下.有一个工作线程必须引发一些事件:

Task.Run(() =>
{
    for(int i = 0; i < 123456789; i++)
    {
        ... // some job
        OnSomeEvent(i);
    }
});
Run Code Online (Sandbox Code Playgroud)

同步上升事件将阻止作业,直到所有事件处理程序完成:

void OnSomeEvent(int i) => SomeEvent?.Invoke(this, new SomeEventArgs(i));
Run Code Online (Sandbox Code Playgroud)

异步事件上升不会再阻止工作了(耶!)

void OnSomeEvent(int i) => Task.Run(() => SomeEvent?.Invoke(this, new SomeEventArgs(i)));
Run Code Online (Sandbox Code Playgroud)

但现在还有另一个问题:未按正确顺序收到事件:

OnSomeEvent(1);
OnSomeEvent(2);
OnSomeEvent(3);
...

// event handler
SomeEvent += (s, e) => Console.WriteLine(e.I);

// possible output
1
3
2
Run Code Online (Sandbox Code Playgroud)

问题:如何实现以正确顺序发生的异步事件上升?

最近我学会了什么Dispatcher.InvokeAsync 使用队列.看起来我必须做类似的事情.如果我必须这样做:1)它应该是呼叫者的工作还是2)我是否应该同步保持上升事件,接收者必须组织生产者/消费者以防止阻塞工作?或许还有另一种方式?

PS:这与ContinueWhith... 无关..除非存储任务列表是一个合适的解决方案.我关注的是如何实现发射并忘记事件,其中:a)呼叫者未被阻止2)事件以相同的顺序接收.

PPS:我不知道如何让MCVE重现这个问题.它出现在具有大量UI,大量线程等的真实项目中.

Ser*_*rvy 7

您可以使用以下TaskQueue命令将异步操作添加到队列,以便在队列中的上一项完成时启动每个操作:

public class TaskQueue
{
    private Task previous = Task.FromResult(false);
    private object key = new object();

    public Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
    {
        lock (key)
        {
            var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
            previous = next;
            return next;
        }
    }
    public Task Enqueue(Func<Task> taskGenerator)
    {
        lock (key)
        {
            var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
            previous = next;
            return next;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许你写:

private TaskQueue taskQueue = new TaskQueue();
private void OnSomeEvent(int i) => 
    taskQueue.Enqueue(() => Task.Run(() => SomeEvent?.Invoke(this, new SomeEventArgs(i))));
Run Code Online (Sandbox Code Playgroud)