C# Rx在不同线程上生产和消费

C# Rx在不同线程上生产和消费,c#,.net-4.0,system.reactive,C#,.net 4.0,System.reactive,我试图通过这里的示例代码来简化我的问题。我有一个producer线程不断地输入数据,我试图在批处理之间有一个时间延迟来批处理数据,以便UI有时间呈现数据。但结果并不像预期的那样,产品和消费者似乎处于同一条线上 我不希望批处理缓冲区在正在生成的线程上休眠。尝试订阅没有多大帮助。我做错了什么,我如何让它在生产者和消费者线程上打印不同的线程ID static void Main(string[] args) { var stream = new ReplaySubject<int>

我试图通过这里的示例代码来简化我的问题。我有一个producer线程不断地输入数据,我试图在批处理之间有一个时间延迟来批处理数据,以便UI有时间呈现数据。但结果并不像预期的那样,产品和消费者似乎处于同一条线上

我不希望批处理缓冲区在正在生成的线程上休眠。尝试订阅没有多大帮助。我做错了什么,我如何让它在生产者和消费者线程上打印不同的线程ID

static void Main(string[] args)
{
    var stream = new ReplaySubject<int>();

    Task.Factory.StartNew(() =>
    {
        int seed = 1;
        while (true)
        {
            Console.WriteLine("Thread {0} Producing {1}",
                Thread.CurrentThread.ManagedThreadId, seed);

            stream.OnNext(seed);
            seed++;

            Thread.Sleep(TimeSpan.FromMilliseconds(500));
         }
    });

    stream.Buffer(5).Do(x =>
    {
        Console.WriteLine("Thread {0} sleeping to create time gap between batches",
            Thread.CurrentThread.ManagedThreadId);

        Thread.Sleep(TimeSpan.FromSeconds(2));
    })
    .SubscribeOn(NewThreadScheduler.Default).Subscribe(items =>
    {
        foreach (var item in items)
        {
            Console.WriteLine("Thread {0} Consuming {1}",
                Thread.CurrentThread.ManagedThreadId, item);
        }
    });
    Console.Read();
}
static void Main(字符串[]args)
{
var stream=新的ReplaySubject();
Task.Factory.StartNew(()=>
{
int种子=1;
while(true)
{
WriteLine(“线程{0}产生{1}”,
Thread.CurrentThread.ManagedThreadId,种子);
stream.OnNext(种子);
seed++;
睡眠(时间跨度从毫秒(500));
}
});
stream.Buffer(5.Do)(x=>
{
WriteLine(“线程{0}正在休眠以在批处理之间创建时间间隔”,
Thread.CurrentThread.ManagedThreadId);
线程睡眠(TimeSpan.FromSeconds(2));
})
.SubscribeOn(NewThreadScheduler.Default).Subscribe(items=>
{
foreach(项目中的var项目)
{
WriteLine(“线程{0}消耗{1}”,
Thread.CurrentThread.ManagedThreadId,项目);
}
});
Console.Read();
}

观测可能就是您想要的。它以SynchronizationContext作为参数,该参数应该是UI的SynchronizationContext。如果您不知道如何获取它,请参见此处的关键部分,了解
ObserveOn
SubscribeOn
之间的区别。请参阅-以获取对这些问题的深入解释

另外,您绝对不希望在Rx中使用
线程.Sleep
。或者任何地方。永远<编码>做
几乎是邪恶的,但ad.睡眠几乎总是完全邪恶的。缓冲区中有一些您想使用的重载-这些重载包括基于时间的重载和接受计数限制和时间限制的重载,当达到其中任何一个时返回缓冲区。基于时间的缓冲将在生产者和消费者之间引入必要的并发性——也就是说,在生产者之外的单独线程上将缓冲交付给它的订户

还可以看到这些问题和答案,它们在保持消费者响应方面进行了很好的讨论(在这里的WPF上下文中,但要点通常是适用的)

上面的最后一个问题具体使用了基于时间的缓冲区重载。正如我所说,在调用链中使用
Buffer
ObserveOn
将允许您在生产者和消费者之间添加并发性。您仍然需要注意,缓冲区的处理速度仍然足够快,以免在缓冲区订阅服务器上建立队列

如果队列确实在增加,您需要考虑施加反压力、删除更新和/或合并更新的方法。这是一个太宽泛的大话题,无法在这里进行深入讨论,但基本上你可以:

  • 删除事件。在Rx中已经讨论了许多方法来解决这个问题。我现在喜欢,但也看到了,还有很多其他的讨论
  • 向带外制作人发出信号,让其减速或发送合并的更新,或
  • 您引入了一个进行流内合并的操作符,比如一个更智能的
    缓冲区
    ,它可以压缩事件,例如,只包括股票项目的最新价格等。例如,您可以编写对
    OnNext
    调用处理时间敏感的操作符

看看适当缓冲是否有帮助,然后考虑源上的节流/合并事件(UI只能显示这么多信息)——然后考虑更聪明的合并,因为这会变得相当复杂。是使用一些高级合并技术的项目的一个很好的例子。

尽管其他答案是正确的,但我想指出您的实际问题可能是对Rx行为的误解。将生产者置于睡眠状态会阻止对
OnNext
的后续调用,并且似乎假设Rx会同时自动调用
OnNext
,但事实上,这不是出于很好的原因。实际上,Rx有一个需要序列化通知的合同

有关详细信息,请参见中的§§4.2、6.7

最终,看起来好像您正在尝试从Rxx实现操作符。此操作符允许您传入一个并发引入调度器,类似于
ObserveOn
,以在生产者和消费者之间创建并发边界
BufferIntrospective
是一种动态背压策略,它根据观察者不断变化的延迟推出大小不等的批次。当观察者处理当前批时,操作员缓冲所有传入的并发通知。为此,操作员利用
OnNext
是一个阻塞调用(根据§4.2合同)这一事实,因此该运算符应尽可能靠近查询边缘,通常在您调用
Subscribe
之前应用


正如James所描述的,您可以将其本身称为“智能缓冲”策略,或者将其视为实施此类策略的基线;e、 例如,我还定义了一个
SampleIntrospective
操作符,它删除每个批中除最后一个通知之外的所有通知。

我无法删除事件或获取最新值,我需要将所有消息发送到UI,因为它们形成单独的行。加上观察者似乎在耍把戏,为什么说睡觉是邪恶的?是否有更好的方法在每个批次之间创建延迟?@anivas-您可以通过usi造成延迟