【发布时间】:2017-09-07 23:28:20
【问题描述】:
我有一个程序不断地从 WebSocket 读取和解析大量数据流。所有的解析都发生在客户端的一个线程上,数据被组织成一个SortedSet<T>树以便快速操作。
所有数据都可以顺利添加、更新和删除。
当我尝试从另一个线程访问数据时,问题就出现了。它会运行良好,但在某处可能会在一两分钟内达到比赛条件。
考虑使用这段代码(在自己的线程上运行)来近乎实时地更新 UI:
private async Task RenderOrderBook()
{
var book = _client.OrderBook;
while (true)
{
try
{
var asks = book.Asks.OrderBy(i => i.Price).Take(5).OrderByDescending(i => i.Price);
var bids = book.Bids.OrderByDescending(i => i.Price).Take(5);
orderBookView.BeginInvoke(new MethodInvoker(() =>
{
...omitted due to irrelevance
}));
await Task.Delay(500);
}
catch (Exception ex)
{
ex.ToString();
}
}
}
竞争条件位于book 上的 LINQ 操作中。常见的错误是 i.Price(decimal 变量),或者可能只是 i 所指的对象,为空。此外,我只是吞下异常的粗制滥造的尝试实际上并没有奏效。
无论如何,我的猜测是数据被解析和操作的速度如此之快,以至于最终,当使用 LINQ OrderBy 操作时,它会遇到一个节点已被客户端删除并尝试从中读取的情况,并且抛出异常。
book.Asks 和 book.Bids 属性最初的类型为 SortedSet<T>,并直接指向数据成员本身。为了缓解这种竞争情况,我尝试将它们更改为节点数组,并使用_asks.ToArray() 调用实质上制作副本以供读取。这有助于降低问题的发生频率,但它仍然会发生。
我怎样才能使这个线程安全?
其他代码片段
public PriceNode[] Asks
{
get { return _asks.ToArray(); }
}
public PriceNode[] Bids
{
get { return _bids.ToArray(); }
}
【问题讨论】:
-
使用像
ConcurrentDictionary这样的线程安全集合,或者像 System.Immutable nuget 包中那样的不可变集合可能会让你完成 90% 的工作。 -
您显示的代码中没有任何内容与线程或竞争条件有任何问题。唯一可能存在问题的地方是
...omitted due to irrelevance代码。是否可以显示该代码?
标签: c# multithreading linq thread-safety