[英]Refactoring code with a lot of “locks” to more lock-free code
Upd thanks to Matthew Watson for noticing and note that I plan to port my code to c++-linux so I prefer "platform-independent" code 更多感谢Matthew Watson注意到并注意到我打算将我的代码移植到c ++ - linux所以我更喜欢“平台无关”的代码
My trading application is almost lock-free. 我的交易应用程序几乎无锁。 The code below is the only place where I do use locks. 下面的代码是我唯一使用锁的地方。 Let me start with the code, it's pretty long but don't worry there are a lot of repeating parts so it's simple. 让我从代码开始,它很长但不要担心有很多重复部分,所以它很简单。 I prefer to add all "repeating" parts to better demonstrate how my things work: 我更喜欢添加所有“重复”部分,以更好地展示我的工作方式:
Task.Factory.StartNew(() =>
{
while (true)
{
Iterate();
}
}, TaskCreationOptions.LongRunning);
private void Iterate()
{
bool marketDataUpdated = false;
lock (ordersToRegisterLock)
{
if (ordersToRegister.Count > 0)
{
marketDataUpdated = true;
while (ordersToRegister.Count > 0)
{
Order order = ordersToRegister.Dequeue();
// Stage1, Process
}
}
}
lock (aggrUpdatesLock)
{
if (aggrUpdates.Count > 0)
{
marketDataUpdated = true;
while (!aggrUpdates.IsNullOrEmpty())
{
var entry = aggrUpdates.Dequeue();
// Stage1, Process
}
}
}
lock (commonUpdatesLock)
{
if (commonUpdates.Count > 0)
{
marketDataUpdated = true;
while (!commonUpdates.IsNullOrEmpty())
{
var entry = commonUpdates.Dequeue();
// Stage1, Process
}
}
}
lock (infoUpdatesLock)
{
if (infoUpdates.Count > 0)
{
marketDataUpdated = true;
while (!infoUpdates.IsNullOrEmpty())
{
var entry = infoUpdates.Dequeue();
// Stage1, Process
}
}
}
lock (tradeUpdatesLock)
{
if (tradeUpdates.Count > 0)
{
marketDataUpdated = true;
while (!tradeUpdates.IsNullOrEmpty())
{
var entry = tradeUpdates.Dequeue();
// Stage1, Process
}
}
}
if (marketDataUpdated)
{
// Stage2 !
// make a lot of work. expensive operation. recalculate strategies, place orders etc.
}
}
private readonly Queue<Order> ordersToRegister = new Queue<Order>();
private readonly object ordersToRegisterLock = new object();
private readonly Queue<AggrEntry> aggrUpdates = new Queue<AggrEntry>();
private readonly object aggrUpdatesLock = new object();
private readonly Queue<CommonEntry> commonUpdates = new Queue<CommonEntry>();
private readonly object commonUpdatesLock = new object();
private readonly Queue<InfoEntry> infoUpdates = new Queue<InfoEntry>();
private readonly object infoUpdatesLock = new object();
private readonly Queue<TradeEntry> tradeUpdates = new Queue<TradeEntry>();
private readonly object tradeUpdatesLock = new object();
public void RegistorOrder(object sender, Gate.RegisterOrderArgs e)
{
lock (ordersToRegisterLock)
{
ordersToRegister.Enqueue(e.order);
}
}
public void TradeUpdated(object sender, Gate.TradeArgs e)
{
lock (tradeUpdatesLock)
{
foreach (var entry in e.entries)
{
tradeUpdates.Enqueue(entry);
}
}
}
public void InfoUpdated(object sender, Gate.InfoArgs e)
{
lock (infoUpdatesLock)
{
foreach (var entry in e.entries)
{
infoUpdates.Enqueue(entry);
}
}
}
public void CommonUpdated(object sender, Gate.CommonArgs e)
{
lock (commonUpdatesLock)
{
foreach (var entry in e.entries)
{
commonUpdates.Enqueue(entry);
}
}
}
public void AggrUpdated(object sender, Gate.AggrArgs e)
{
lock (aggrUpdatesLock)
{
foreach (var entry in e.entries)
{
aggrUpdates.Enqueue(entry);
}
}
}
In my code I have two stages. 在我的代码中,我有两个阶段。 Stage1
is update stage and Stage2
is working stage. Stage1
是更新阶段, Stage2
处于工作阶段。 I need to switch between these two stages as fast as possible, like that: 我需要尽快在这两个阶段之间切换,如下所示:
Stage2
应用更新,做Stage2
Stage2
应用更新,做Stage2
Stage2
交易发生,应用更新,做Stage2
In Stage2
I should not update, but should keep "collecting" updates so I can apply they later. 在Stage2
我不应该更新,但应该保持“收集”更新,以便我可以在以后应用它们。
And important thing - this is very latency-critical code so I agree to "spent" one core for having minimal latency! 重要的是 - 这是非常关键的延迟代码,因此我同意“花费”一个核心以获得最小的延迟! So when any update occure I need to process it asap and perform Stage2
. 因此,当发生任何更新时,我需要尽快处理它并执行Stage2
。
So I hope now it's clear what I need to achieve and it's clear how I have implemented that. 所以我希望现在很清楚我需要实现什么,而且很清楚我是如何实现的。 Now it's time to discuss how good my code is. 现在是讨论我的代码有多好的时候了。 I do see several potential problems: 我确实看到了几个潜在的问题:
Any suggestions how to improve what I wrote are welcome, thanks! 欢迎提出任何有关如何改进我写作的建议,谢谢!
upd partly solved - as I understand I better to replace queries to lock-free (likely ring-buffer based?) queries.. i think i will use c++ version of disruptor later. upd部分解决了 - 据我所知,我最好将查询替换为无锁(可能是基于环缓冲的?)查询。我想我稍后会使用c ++版本的disruptor。 Also I've used this article http://www.umbraworks.net/bl0g/rebuildall/2010/03/08/Running_NET_threads_on_selected_processor_cores and replaced Task with a Thread running on the "fixed" core, however i'm still using "busy-spin", probably I should use something smarter? 我也使用了这篇文章http://www.umbraworks.net/bl0g/rebuildall/2010/03/08/Running_NET_threads_on_selected_processor_cores并用“固定”核心上运行的线程替换了任务,但是我还在使用“忙” -spin“,可能我应该使用更聪明的东西?
With the code below, you are no longer locked during "stage 1" processing: 使用下面的代码,您在“阶段1”处理期间不再被锁定:
Task.Factory.StartNew(() =>
{
while (true)
{
Iterate();
}
}, TaskCreationOptions.LongRunning);
private void Iterate()
{
bool marketDataUpdated = false;
foreach (Order order in ordersToRegister)
{
marketDataUpdated = true;
// Stage1, Process
}
foreach (var entry in aggrUpdates)
{
marketDataUpdated = true;
// Stage1, Process
}
foreach (var entry in commonUpdates)
{
marketDataUpdated = true;
// Stage1, Process
}
foreach (var entry in infoUpdates)
{
marketDataUpdated = true;
// Stage1, Process
}
foreach (var entry in tradeUpdates)
{
marketDataUpdated = true;
// Stage1, Process
}
if (marketDataUpdated)
{
// Stage2 !
// make a lot of work. expensive operation. recalculate strategies, place orders etc.
}
}
private readonly ConcurrentQueue<Order> ordersToRegister = new ConcurrentQueue<Order>();
private readonly ConcurrentQueue<AggrEntry> aggrUpdates = new ConcurrentQueue<AggrEntry>();
private readonly ConcurrentQueue<CommonEntry> commonUpdates = new ConcurrentQueue<CommonEntry>();
private readonly ConcurrentQueue<InfoEntry> infoUpdates = new ConcurrentQueue<InfoEntry>();
private readonly ConcurrentQueue<TradeEntry> tradeUpdates = new ConcurrentQueue<TradeEntry>();
public void RegistorOrder(object sender, Gate.RegisterOrderArgs e)
{
ordersToRegister.Enqueue(e.order);
}
public void TradeUpdated(object sender, Gate.TradeArgs e)
{
foreach (var entry in e.entries)
{
tradeUpdates.Enqueue(entry);
}
}
public void InfoUpdated(object sender, Gate.InfoArgs e)
{
foreach (var entry in e.entries)
{
infoUpdates.Enqueue(entry);
}
}
public void CommonUpdated(object sender, Gate.CommonArgs e)
{
foreach (var entry in e.entries)
{
commonUpdates.Enqueue(entry);
}
}
public void AggrUpdated(object sender, Gate.AggrArgs e)
{
foreach (var entry in e.entries)
{
aggrUpdates.Enqueue(entry);
}
}
Here is an approach that might be more portable. 这是一种可能更便携的方法。 Hope it helps. 希望能帮助到你。
public class SafeQueue<T> : Queue<T>
{
public T SafeDequeue()
{
lock (this)
{
return (Count > 0) ? Dequeue() : null;
}
}
public void SafeEnqueue(T entry)
{
lock (this)
{
Enqueue(entry);
}
}
}
Task.Factory.StartNew(() =>
{
while (true)
{
Iterate();
}
}, TaskCreationOptions.LongRunning);
private void Iterate()
{
bool marketDataUpdated = false;
while ((Order order = ordersToRegister.SafeDequeue()) != null)
{
marketDataUpdated = true;
// Stage1, Process
}
while ((var entry = aggrUpdates.SafeDequeue()) != null)
{
marketDataUpdated = true;
// Stage1, Process
}
while ((var entry = commonUpdates.SafeDequeue()) != null)
{
marketDataUpdated = true;
// Stage1, Process
}
while ((var entry = infoUpdates.SafeDequeue()) != null)
{
marketDataUpdated = true;
// Stage1, Process
}
while ((var entry = tradeUpdates.SafeDequeue()) != null)
{
marketDataUpdated = true;
// Stage1, Process
}
if (marketDataUpdated)
{
// Stage2 !
// make a lot of work. expensive operation. recalculate strategies, place orders etc.
}
}
private readonly SafeQueue<Order> ordersToRegister = new SafeQueue<Order>();
private readonly SafeQueue<AggrEntry> aggrUpdates = new SafeQueue<AggrEntry>();
private readonly SafeQueue<CommonEntry> commonUpdates = new SafeQueue<CommonEntry>();
private readonly SafeQueue<InfoEntry> infoUpdates = new SafeQueue<InfoEntry>();
private readonly SafeQueue<TradeEntry> tradeUpdates = new SafeQueue<TradeEntry>();
public void RegistorOrder(object sender, Gate.RegisterOrderArgs e)
{
ordersToRegister.SafeEnqueue(e.order);
}
public void TradeUpdated(object sender, Gate.TradeArgs e)
{
foreach (var entry in e.entries)
{
tradeUpdates.SafeEnqueue(entry);
}
}
public void InfoUpdated(object sender, Gate.InfoArgs e)
{
foreach (var entry in e.entries)
{
infoUpdates.SafeEnqueue(entry);
}
}
public void CommonUpdated(object sender, Gate.CommonArgs e)
{
foreach (var entry in e.entries)
{
commonUpdates.SafeEnqueue(entry);
}
}
public void AggrUpdated(object sender, Gate.AggrArgs e)
{
foreach (var entry in e.entries)
{
aggrUpdates.SafeEnqueue(entry);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.