我遇到了Rx.Window
的问题以下是我的代码。请注意,某些工作项未达到内部订阅。
var subscription
= m_BufferBlock
.AsObservable()
.Synchronize()
.Where(InValue => InValue.Region == Region)
.Do(W => logger.Debug("Side Effect => " + W.ToString()))
此后工作项目丢失;不打窗户,我不明白它 在订阅内收到
.Window(TimeSpan.FromMilliseconds(10000))
.SubscribeOn(Scheduler.Default)
.Subscribe(window =>
{
window
.ToList()
.SubscribeOn(Scheduler.Default)
.Subscribe(workItems =>
{
foreach (WorkItem W in workItems)
{
// Some work items do not reach this line
logger.Debug("Came inside subscriber => " + W);
}
if (workItems.Count > 0)
{
ProcessWorkItems(workItems.ToList<WorkItem>());
}
});
});
WorkItem类
public class WorkItem
{
public int Region { get; set; }
public int Priority { get; set; }
public string PortfolioId { get; set; }
public string SecurityId { get; set; }
public string Status { get; set; }
public string Message { get; set; }
public Int64 Guid { get; set; }
public DateTime RequestedDateTime { get; set; }
public WorkItemType WorkItemType { get; set; }
public long RecordCount { get; set; }
public WorkItem()
{
PortfolioId = string.Empty;
SecurityId = string.Empty;
Message = string.Empty;
Status = string.Empty;
Region = 0;
WorkItemType = WorkItemType.REALTIME;
RequestedDateTime = DateTime.Now;
RecordCount = 0;
}
public override string ToString()
{
return string.Format("WorkItemType : {0} Region : {1} PortfolioId : {2} SecurityId : {3} Guid : {4} Priority : {5} Status : {6} Message : {7}",
WorkItemType.ToString(), Region, PortfolioId, SecurityId, Guid, Priority, Status, Message);
}
}
我正在使用具有无限容量的BufferBlock ......
任何帮助将不胜感激......
答案 0 :(得分:3)
不要window.ToList().SubscribeOn(...).Subscribe(...)
。而是使用window.ToList().Subscribe(...)
或使用使用SelectMany的Chris版本。
内部窗口可观察量热。这意味着您必须在返回之前订阅它们,否则您将丢失窗口中的项目。但是,SubscribeOn在订阅中引入了延迟,而对另一个线程或任务调度订阅。这意味着您在实际订阅窗口之前从回调中返回,并且在项目到达窗口之前查看订阅是否运行是竞赛。如果某个项目在预定的预订运行调用之前到达,则该项目会因为尚未设置订阅而丢失。
因此,正如James所说,不仅SubscribeOn没用,它实际上是你问题的原因。
答案 1 :(得分:2)
你绝对确定你正在失去元素吗? (如果你是,我几乎可以肯定它不是Window
)
我问的原因是你的Rx查询的编写方式使得在窗口n + 1的Do()
中发送的日志输出完全可以在捕获的项目之前写出窗口n已被记录。
我想知道在日志中进一步阅读可能会解决问题吗?
要检查这一点,我们可以稍微修改您的查询以获取窗口编号并将其包含在您的日志输出中,如下所示:
var subscription
= m_BufferBlock
.AsObservable()
.Synchronize()
.Where(InValue => InValue.Region == Region)
.Do(W => logger.Debug("Side Effect => " + W.ToString()))
.Window(TimeSpan.FromMilliseconds(10000))
.Select((window, index) => Tuple.Create(window,index))
.SubscribeOn(Scheduler.Default)
.Subscribe(window =>
{
window.Item1
.ToList()
.SubscribeOn(Scheduler.Default)
.Subscribe(workItems =>
{
foreach (WorkItem W in workItems)
{
// Some work items do not reach this line
logger.Debug("Came inside window " + window.Item2 + " and subscriber => " + W);
}
if (workItems.Count > 0)
{
ProcessWorkItems(workItems.ToList<WorkItem>());
}
});
});
我应该以你所拥有的方式添加嵌套订阅不是最佳实践。它带来了线性问题(你在这里遇到的影响),性能,灵活性和可读性。你最好保持“monad”(即组成可观察者)并尽可能长时间地进行最终订阅。
看看Chris如何使用SelectMany
代替嵌套订阅来翻译您的查询,以了解如何处理此问题。此外,您的SubscribeOn
可能在这里没有任何帮助 - Have a look at this question to see what SubscribeOn
gives you.
答案 2 :(得分:0)
Window
都会为每个窗口期间产生一个Observable。因此,您可能最终得到不包含任何事件的窗口。当你在这些窗口上使用ToList时,它们将产生一个可观察的空列表。
var subscription = m_BufferBlock
.AsObservable()
.Synchronize()
.Where(item => item.Region == Region)
.Do(item => logger.Debug("Side Effect => " + item))
.Window(TimeSpan.FromMilliseconds(10000))
.SelectMany(window => window.ToList())
.Where(workItems => workItems.Count > 0)
.Subscribe(ProcessWorkItems);
这也可以。
var subscription = m_BufferBlock
.AsObservable()
.Synchronize()
.Where(item => item.Region == Region)
.Do(item => logger.Debug("Side Effect => " + item))
.Buffer(TimeSpan.FromMilliseconds(10000))
.Where(workItems => workItems.Count > 0)
.Subscribe(ProcessWorkItems);