Многопоточность тупиковая проблема - PullRequest
0 голосов
/ 23 марта 2020

Обновление: я не являюсь обычным пользователем StackOverflow, поэтому извиняюсь, но я не вижу способа отправить это лично по электронной почте модераторам. Я думаю, что было очень несправедливо и жестко блокировать эту тему как отключенную topi c и говоря, что я должен опубликовать пример кода et c. Я уже объяснил, что это большое сложное приложение, мне пришлось бы публиковать десятки строк кода, это было бы непонятно для всех. Не все программные проблемы настолько просты, что сводятся к 10 строкам примера кода!

Фундаментальная проблема - архитектурная, я изо всех сил пытался объяснить, что у меня есть 2 асин c потока, которые должны заблокировать два ресурсы, затем эти 2 блокировки также доступны в главном потоке. Из-за асин c природы. Я не могу понять, как я могу контролировать порядок блокировок и чередование двух блокировок, чтобы всегда гарантировать отсутствие взаимоблокировок.

Да, как и предполагалось, я могу сделать всего одну блокировку, чтобы заблокировать все, но снижение производительности было бы очень плохим, поэтому я не могу выбрать это простое решение. Я надеялся, что есть более производительное решение classi c, когда вам нужно несколько блокировок в сценарии asyn c.

В любом случае, спасибо за ответы, которые я получил, до того как он был закрыт, я ценю ваше время и помогите в эти трудные времена.

С уважением


Я пишу довольно большое приложение в C#, оно имеет 2 асинхронных потока, один получает поток данных о цене товара, и один получает подробный поток заказа.

Каждый из этих потоков строит список входящих данных о цене / заказе, каждый из которых использует свой собственный ReaderWriterLockSlim. Устанавливается в режиме блокировки записи, когда входящие данные перемещаются в списки.

Частота входящих потоков очень различна и непредсказуема, иногда пакеты цены товара могут поступать со скоростью 10 i sh миллис. c интервалов, так что мне нужно эффективно кодировать, иначе получится очень медлительно sh.

Изначально я просто читал списки в главном потоке (блокировка чтения ReaderWriterLockSlim), это работало нормально. Однако теперь у меня есть требование, чтобы в главном потоке иногда приходилось делать записи в оба списка данных. Да, как вы уже догадались, с дополнительными блокировками записи, я теперь получаю случайные взаимоблокировки, я уверен, что это классическая блокировка c, вызванная чередованием доступа к двум отдельным блокировкам.

Из-за самой асинхронности c характер моих входящих данных, я не могу предсказать, когда эти потоки будут записывать или читать блокировку списков, в зависимости от того, какую обработку мой основной поток выполняет в двух списках.

Насколько я могу видите, это всегда будет склонно к тупику. Я ужасно застрял в том, как я могу изменить архитектуру для решения этой тупиковой проблемы.

Я был бы очень признателен, если бы кто-нибудь мог высказать некоторые мысли о том, как эта проблема может быть решена.


Пример кода. Один поток запускает этот код:

lock (locker1)
{
    list1.Add("Something");
    lock (locker2)
    {
        list2.Add("Something");
    }
}

... в то время как другой поток запускает этот код:

lock (locker2)
{
    list2.Add("Something");
    lock (locker1)
    {
        list1.Add("Something");
    }
}

Можно ли избежать следующих тупиков, не вводя ограничений в том порядке, что замки взяты?

Ответы [ 2 ]

2 голосов
/ 23 марта 2020

(1) трудно сказать, в чем проблема, когда вы описываете код вместо его показа.

(2) в> 80% случаев, когда возникает блокировка, когда несколько блокировок заблокированы различные потоки в неправильном порядке, например: у вас есть блокировки A, B, C, и поток 1 пытается одновременно собрать блокировки для A, B, C, а поток 2 пытается собрать блокировки для B, A , C.

Убедитесь, что все потоки блокируют свои блокировки в одинаковом порядке. Даже если один из потоков не нуждается в 'A', убедитесь, что другие блокировки взяты по порядку.

(Обратите внимание, что в примерах ниже AB C означает, что поток пытается получить ВСЕ из них раньше освобождение любого. Это означает: «заблокировать A, заблокировать B, заблокировать C, сделать что-нибудь, освободить все три»)

Время от времени могут происходить взаимоблокировки:

  • thread1: AB, нить 2: BA
  • нить 1: AB C, нить 2: BA C
  • нить 1: AB C, нить 2: CAB

Это потому что есть вероятность, что один поток берет B и ждет A, в то время как другой поток берет A и ждет B.

С другой стороны, они НИКОГДА не будут тупиковыми:

  • thread1 : A, нить 2: AB
  • нить 1: A, нить 2: AB C
  • нить 1: AB, нить 2: B C
  • нить 1: AB C , резьба 2: AB C
  • резьба 1: AB C, резьба 2: A C
  • резьба 1: B C, резьба 2: AB C

Это потому что замки взяты строго в заказ. Нет никакого способа, которым поток мог бы попытаться взять A, уже держа B, и т. Д.

В случае, если это не очевидно, правило basi c состоит в том, что строгий порядок попыток заблокировать вещи - это то, что предотвращает тупик, следовательно, либо: (a) заблокируйте только одну вещь за раз, и разблокируйте ее перед любой другой блокировкой, или (b) убедитесь, что все ресурсы, необходимые для данной операции, всегда получены в строгом порядке - у вас есть операция, которая блокирует три списки? убедитесь, что эти списки всегда заблокированы в одинаковом порядке. 3 из 100 списков нужны наугад? Ничего. Заказать их. Убедитесь, что они заблокированы в том же порядке. Если в крайнем случае, используйте их идентификаторы, имена или даже адреса, что угодно, просто убедитесь, что ни один поток не пытается заблокировать список (N + 1) -й до списка (N) -го. Это простое решение, и оно может быть не лучшим по производительности, но, по крайней мере, дает хорошую безопасную отправную точку, которая не требует полной сериализации / et c, и где вы можете опробовать другие схемы и / или диагностировать другие проблемы.

0 голосов
/ 23 марта 2020

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

Таким образом, вы будете обрабатывать один поток за раз, и у вас не возникнет никаких проблем с блокировкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...