RX:如何并行化一些长时间运行的任务并同步其他任务

时间:2014-07-22 10:23:02

标签: .net multithreading system.reactive

我现在已经挣扎了一段时间了。我有我想要异步处理的事件(它们长时间运行),但是如果从事件传递的数据满足条件(例如相同的id),我希望同步处理事件按顺序。

这是否可以通过RX轻松实现?或者我是否需要实现自己的线程同步和排队?

以下是说明我的问题的代码段:

    class Program
{
    static void Main(string[] args)
    {
        var trigger = new EventTrigger();

        Observable.FromEvent<EventTrigger.OnNewDataHandler, int>(h => trigger.OnNewData += h, h => trigger.OnNewData -= h)
                  .Subscribe(LongRunning);

        // Should process id 1 synchorously and in order, and ids 2, 3, 4 in parallel
        trigger.RaiseEvent(1);
        trigger.RaiseEvent(2);
        trigger.RaiseEvent(1);
        trigger.RaiseEvent(1);
        trigger.RaiseEvent(3);
        trigger.RaiseEvent(4);

        Console.ReadKey();
    }

    private static void LongRunning(int id)
    {
        var random = new Random();
        Thread.Sleep(random.Next(1, 10) * 1000);
        Console.WriteLine(DateTime.Now + ": " + id);
    }
}

public class EventTrigger
{
    public event OnNewDataHandler OnNewData;
    public delegate void OnNewDataHandler(int id);

    public void RaiseEvent(int id)
    {
        var handler = OnNewData;
        if (handler != null) handler(id);
    }
}

3 个答案:

答案 0 :(得分:2)

这可以通过内部使用Rx的Punchclock轻松完成。这是唯一一个班级唯一的方法:

IObservable<T> EnqueueObservableOperation(int priority, string key, IObservable<TDontCare> cancel, Func<IObservable<T>> asyncCalculationFunc);

任何具有相同key参数的内容都将按顺序运行,但具有不同键的操作将并行运行。

  

作为一个粗略概括的经验法则,如果你有&#34;并行化&#34;在您的问题中,Rx通常不是答案!

我不同意:))

答案 1 :(得分:1)

Rx保证每个订户的事件按顺序非并发处理,因此您只需使用两个不同的订阅。使用一个用于仅利用Rx语义的同步处理事件。这必须指定一个TaskPoolScheduler(或类似的)来避免阻塞当前线程。然后使用第二个用于卸载到处理程序中任务池的异步处理事件。

    var events = Observable.FromEvent<EventTrigger.OnNewDataHandler, int>(
        h => trigger.OnNewData += h,
        h => trigger.OnNewData -= h);

    events.Where(id => id == 1)
          .ObserveOn(TaskPoolScheduler.Default)
          .Subscribe(LongRunning);

    events.Where(id => id != 1)
          .ObserveOn(TaskPoolScheduler.Default)
          .Subscribe(LongRunningAsync);   

使用处理程序:

private static void LongRunning(int id)
{
    DoWork(id);
}

private static void LongRunningAsync(int id)
{
    Task.Run(() => DoWork(id));        
}

private static void DoWork(int id)
{
    Console.WriteLine(DateTime.Now + ": started " + id);
    var random = new Random();
    Thread.Sleep(random.Next(1, 10) * 1000);
    Console.WriteLine(DateTime.Now + ": finished " + id);
}

答案 2 :(得分:1)

如果您乐意开展工作,然后投射出结果,那么这就行了。

Observable.FromEvent<EventTrigger.OnNewDataHandler, int>(
    h => trigger.OnNewData += h, 
    h => trigger.OnNewData -= h
    )
    .GroupBy(i=>i)
    .Select(grp=>grp.ObserveOn(TaskPoolScheduler.Default).SelectMany(i=>LongRunningTransform(i)))
    .Subscribe(result=>Console.WriteLine(result));

在这里,我们只需将LongRunning OnNext处理程序更改为投影

private static string LongRunningTransform(int id)
{
    var random = new Random();
    Thread.Sleep(random.Next(1, 10) * 1000);
    return string.Format("{0:o} Id: {1} on thread {2}", 
            DateTime.Now ,
            id, 
            Thread.CurrentThread.ManagedThreadId);
}