如何同步执行主题中的功能?

时间:2019-05-13 08:56:24

标签: c# reactive-programming system.reactive

我要解决的问题是,我想从多个线程中启动一个异步操作,但是一次只能执行一个操作。

操作是与硬件设备通信,并且该设备一次只能处理一个请求。

我的想法之一是将其与System.Reactive.Subjects.Subject同步。多个线程可以调用OnNext,而主体应该在另一个请求之后执行一个请求。我写了这个(可能很幼稚)的代码:

static void Main(string[] args)
{
    var source = new System.Reactive.Subjects.Subject<Func<Task<int>>>();

    source
        // from http://code.fitness/post/2016/11/rx-selectmany-deep-dive.html
        .SelectMany(async x => await x.Invoke()) 
        .Subscribe(result => Console.WriteLine($"Work of index {result} completed"));

    var noOfThreads = 3;
    for (var i = 0; i < noOfThreads; i++)
    {
        var i1 = i;
        var t = new Thread(() => source.OnNext(() => doWork(i1)));
        t.Start();
    }

    Console.ReadKey();
}

static async Task<int> doWork(int index)
{
    Console.WriteLine($"Start work {index}");
    await Task.Delay(1000);
    Console.WriteLine($"Stop work {index}");
    return index;
}

我希望是这样的输出:

Start work 2
Stop work 2
Work of index 2 completed
Start work 0
Stop work 0
Work of index 0 completed

相反,我得到了:

Start work 0
Start work 1
Start work 2
Stop work 1
Stop work 0
Work of index 1 completed
Work of index 0 completed
Stop work 2
Work of index 2 completed

这表明所有操作均从头开始,并且没有等待其他操作完成的等待。我想知道Reactive是执行此操作的正确方法,还是我可以通过其他一些聪明的方法来完成任务。

编辑:提供更多背景信息,说明为什么我需要这样做:应用程序与设备通信。该设备具有一个串行接口,一次只能处理一个命令。因此,我有一个线程会不断获取状态更新,例如:

while (true)
{
    ReadPosition();
    ReadTempereatures();
    ReadErrors();
}

然后有一个ui,用户可以在其中启动设备上的某些操作。 我当前的解决方案是将命令排入队列。那行得通,但我想知道事件方法是否也行得通。

1 个答案:

答案 0 :(得分:4)

您正在混合Rx,任务和线程。难怪它正在脱离轨道。选择一种方法-Rx是最好的方法,恕我直言-您应该会没事的。

足够了吗

static void Main(string[] args)
{
    var source = new Subject<Func<int>>();

    source
        .Synchronize()
        .Select(x => x())
        .Subscribe(result => Console.WriteLine($"Work of index {result} completed"));

    var noOfThreads = 3;
    for (var i = 0; i < noOfThreads; i++)
    {
        var i1 = i;
        var t = new Thread(() => source.OnNext(() => doWork(i1)));
        t.Start();
    }

    Console.ReadLine();
}

static int doWork(int index)
{
    Console.WriteLine($"Start work {index}");
    Thread.Sleep(1000);
    Console.WriteLine($"Stop work {index}");
    return index;
}

给出:

Start work 0
Stop work 0
Work of index 0 completed
Start work 2
Stop work 2
Work of index 2 completed
Start work 1
Stop work 1
Work of index 1 completed

关键是调用.Synchronize()以使所有调用线程都处于Rx合同之下。