确保螺纹安全

时间:2010-03-01 19:39:45

标签: c# multithreading threadpool

我正在编写一个C#Windows窗体应用程序,该应用程序通过算法(策略)处理来自市场的报价,以向经纪公司创建订单。在我尝试构建与每个策略同时运行多个策略的能力之前,所有这些似乎都进行了相当好的测试。此时一切都开始运行不正确。我相信我有一些不是线程安全的类,它们会导致不稳定的行为。我非常感谢您对如何以线程安全方式进行线程化的任何见解!

引号输入算法的方式如下: 1)市场数据事件从Brokers Software发布到我的软件中名为ConnectionStatus的客户端类。触发市场数据事件时,将根据表示Bid,ask等的静态变量的当前值构建Quote对象。 一旦构建了引用,我就会努力将其发送到正在运行的每个策略算法中。以下是我用来执行此操作的代码:

 foreach (StrategyAssembler assembler in StrategyAssembleList.GetStrategies())
 {                  
     BackgroundWorker thread = strategyThreadPool.GetFreeThread();
     if (thread != null)
     {
        thread.DoWork += new DoWorkEventHandler(assembler.NewIncomingQuote);
        thread.RunWorkerAsync(quote);
     }   
 }

StrategyAssembler是一个创建Class StrategyManager实例的类,后者又创建了包含实际算法的策略实例。可能有4或6个不同的StrategyAssembler实例,每个实例都已添加到StrategyAssembleList的Singleton实例中,这是一个BindingList。

传入的引用对象被传递到StrategyAssembler类的NewIncomingQuote方法。该代码如下:

public void NewIncomingQuote(object sender, DoWorkEventArgs e)
    {
        Quote QUOTE = e.Argument as Quote;            

        lock (QuoteLocker)
        {
            manager.LiveQuote(QUOTE);

            priorQuote = QUOTE;
        }
    }

我在想通过在将引用传递给manager.LiveQuote(引用引用)方法之前使用锁定,使用此点“下游”引用的所有对象都能够在线程安全中使用引用时尚,但测试显示不然。有没有办法可以将StrategyAssembler的每个实例放在自己的线程上,以确保Strategy Assembler创建的所有对象都是线程安全的,然后将引用提供给StrategyAssembler?这种思维方式是否适合应对这种情况?

提前感谢您的任何反馈或帮助,

Learning1

3 个答案:

答案 0 :(得分:1)

锁定应该在读取和写入任何共享状态时发生。如果没有锁定读取,代码仍然可以同时读写。

您可以将读写锁定包装到管理器中。

答案 1 :(得分:0)

如果:

1)通过LiveQuote方法调用策略,可以修改Quote个实例。

2)Quote实例的更改不应在策略之间共享。

您需要在调用Quote之前创建提供的LiveQuote()的副本,并将副本发送到策略方法而不是原始引用。根据其他要求,您可能根本不需要任何锁定。

答案 2 :(得分:0)

您的代码中发生了两件事:
1.您从一个主题(生产者AKA,市场数据源)收到报价 2.您将报价发送到另一个线程(消费者AKA StrategyAssembler)。

此时报价上存在争用,换句话说,生产者线程和每个消费者线程(即策略的每个实例)都可以修改您刚才提供的报价。为了消除争用,您必须执行以下三项操作之一:

  1. 在所有可以访问报价的线程之间进行同步 或者
  2. 使报价不可变(并确保生产者不会替换它) 或者
  3. 为每位消费者提供一份报价副本。
  4. 对于你的情况,我建议你采取第三种选择,因为锁定比复制报价更昂贵(我希望你的报价不是很大)...选项二也很好,但你的策略不应该修改报价

    通过为每个消费者提供一份报价副本,确保他们不共享任何数据,因此没有其他线程会修改报价,您将消除争用。如果你的策略没有创建任何其他线程,那么你已经完成了。

    一般情况下,您应该避免锁定,并且应该尽量减少数据共享,但如果您必须在线程之间共享数据,那么您应该正确地执行此操作:
    为了使您的策略能够正确同步,必须在同一个 QuoteLocker对象上进行同步,即每个帖子都必须可以看到QuoteLocker。即使你正确地执行它并使你的策略同步(锁定QuoteLocker),你也可能没有线程......你将运行上下文切换+锁定的开销,你的策略将被执行依次为同一个报价。

    每条评论更新 如果您按原样保留代码(意味着您提供了每个线程的报价副本),那么我不明白为什么在第一个策略完成之前您的其他策略不会得到报价...您的第一个策略将是最多的可能在创建其他策略的线程时开始工作。让你的策略在一个单独的线程中运行的重点是避免这个问题......你开始一个新线程,这样你的其他策略就不会等待彼此完成。

    即使在所有线程开始工作之前,这部分代码很可能已完成......

    foreach (StrategyAssembler assembler in StrategyAssembleList.GetStrategies())
     {                  
         BackgroundWorker thread = strategyThreadPool.GetFreeThread();
         if (thread != null)
         {
            thread.DoWork += new DoWorkEventHandler(assembler.NewIncomingQuote);
            Quote copy = CopyTheQuote(quote);// make an exact copy of the quote
            thread.RunWorkerAsync(copy);
         }   
     }
    

    在创建线程时,您的市场Feed是否会更改实际报价?市场供稿通常提供快照,因此除非在您制作线程时某些内容正在改变您的报价,否则上述设计应该没问题。如果设计有问题,那么我可以根据阻塞队列为您提供生产者和多个消费者设计,这也非常有效(you can check out this discussion for an idea on how it works我可以告诉您如何根据您的具体示例修改它)