Обеспечение безопасности нитей - PullRequest
6 голосов
/ 01 марта 2010

Я нахожусь в процессе написания приложения для Windows Form на C #, которое обрабатывает котировки с рынка с помощью алгоритма (стратегии) ​​для создания заказов брокерской фирме. Все это, похоже, тестировалось достаточно хорошо, пока я не попытался создать способность запускать несколько стратегий одновременно с каждой стратегией в своем потоке. В этот момент все начинает работать неправильно. Я считаю, что у меня есть классы, которые не являются поточно-ориентированными и приводят к ошибочному поведению. Любое понимание того, как я могу проделать это в поточно-безопасной манере, очень ценится!

Способ подачи цитат в алгоритмы выглядит следующим образом: 1) Рыночные данные События передаются из Программного обеспечения брокеров в класс клиента в моем Программном обеспечении, называемый ConnectionStatus. Когда инициируется событие рыночных данных, объект Quote создается из текущих значений этих статических переменных, которые представляют Bid, ask и т. Д. После того, как цитата построена, я стараюсь отправить ее в каждый из работающих алгоритмов стратегии. Вот код, который я использую для этого:

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

StrategyAssembler - это класс, который создает экземпляр класса StrategyManager, который, в свою очередь, создает экземпляр стратегии, содержащей фактические алгоритмы. Может существовать 4 или 6 разных экземпляров StrategyAssembler, каждый из которых был добавлен в экземпляр Singleton StrategyAssembleList, представляющий собой BindingList.

Объект входящей цитаты передается в метод NewIncomingQuote класса StrategyAssembler. Этот код выглядит следующим образом:

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

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

            priorQuote = QUOTE;
        }
    }

Я думал, что используя блокировку перед передачей кавычки в метод manager.LiveQuote (Quote quote), все объекты, использующие кавычку "downstream" этой точки, смогут использовать кавычку в потоке безопасно мода, но тестирование показывает обратное. Есть ли способ, которым я мог бы поместить каждый экземпляр StrategyAssembler в свой собственный поток, который гарантирует, что все объекты, созданные Strategy Assembler, являются потокобезопасными, а затем передать цитату в StrategyAssembler? Является ли этот путь мышления подходящим способом справиться с этой ситуацией?

Заранее спасибо за любые отзывы или помощь,

Learning1

Ответы [ 3 ]

1 голос
/ 01 марта 2010

Блокировка должна происходить как при чтении, так и при записи в любое общее состояние. Без блокировок на чтение код все еще может читать и записывать одновременно.

Вы можете обернуть блокировку чтения и записи в диспетчере.

0 голосов
/ 01 марта 2010

В вашем коде происходят две вещи:
1. Вы получили цитату из одной ветки (производитель АКА, рыночный фид данных).
2. Вы отправляете цитату в другую ветку (потребитель AKA StrategyAssembler).

На данный момент существует утверждение о кавычке, другими словами, поток производителя и каждый потребительский поток (т. Е. Каждый экземпляр стратегии) ​​может изменить цитату, которую вы только что предоставили. Для того, чтобы вы удалили утверждение, вы должны сделать одну из трех вещей:

  1. Синхронизация между всеми потоками с доступом к цитате.
    ИЛИ
  2. Сделайте цитату неизменной (и убедитесь, что производитель не заменит ее).
    ИЛИ
  3. Дайте каждому потребителю копию цитаты.

В вашем случае я бы посоветовал вам воспользоваться третьим вариантом, потому что блокировка обходится дороже, чем копирование цитаты (надеюсь, ваши цитаты не очень большие) ... второй вариант тоже хорош, но ваша стратегия не должна изменять цитату .

Предоставляя каждому потребителю копию предложения, вы гарантируете, что он не передает никакие данные, поэтому никакой другой поток не изменит предложение, и вы устраните конфликт. Если ваши стратегии не создают никаких других потоков, то вы закончили.

Как правило, вам следует избегать блокировок и стараться свести к минимуму обмен данными, но если вы ДОЛЖНЫ * обмениваться данными между потоками, вам следует сделать это правильно:
Для правильной синхронизации ваших стратегий они должны синхронизироваться на одном и том же объекте 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);
     }   
 }

Изменяет ли ваш рыночный фид фактическую котировку при создании тем? Рыночные каналы обычно предоставляют моментальные снимки, поэтому, если что-то не меняет вашу цитату, пока вы создаете темы, то приведенный выше дизайн должен быть просто в порядке. Если есть проблема с дизайном, то я могу дать вам дизайн для нескольких производителей и потребителей, основанный на очереди блокировки, которая также очень эффективна (, вы можете проверить это обсуждение для идеи о том, как это работает и я могу рассказать вам, как изменить его для вашего конкретного примера).

0 голосов
/ 01 марта 2010

Если:

1) Стратегии вызываются методом LiveQuote и могут изменять Quote экземпляров.

2) Изменения в Quote экземплярах не должны быть общими для стратегий.

Вам необходимо создать копию предоставленного Quote перед вызовом LiveQuote() и отправить копию в метод стратегии, а не в исходную цитату. В зависимости от других требований блокировка может вообще не понадобиться.

...