|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Доброго времени суток. В .NET синхронизировать процессы можно при помощи именованных мьютексов и семафоров. Экспериментирую по обозначенной теме. Например, пусть два процесса выполняют запись в один и тот же текстовый файл: первый процесс пишет некоторое количество раз слово "dog", а второй - слово "cat". При этом нужно организовать работу процессов так, чтобы слова чередовались. Т.е. на выходе должен получиться файл со следующим содержимым: Ожидаемый текстовый файл dog cat dog cat dog cat dog cat ... dog cat Моё решение выглядит следующим образом: Первое приложение (Launcher.exe) создаёт именованный мьютекс и блокирует его. Затем создаются два идентичных процесса (ConsoleApplication2.exe), синхронизация которых должна происходить через обозначенный мьютекс. Когда процессы созданы, с мьютекса снимается блокировка и процессы запускаются... Однако по факту начало текстового файла начинается не так, как мне бы того хотелось. Например, это может выглядеть так: Фактический текстовый файл dog dog dog dog cat dog cat dog cat dog cat dog cat dog cat ... Т.е. согласно результату, в моём коде синхронизация происходит не сразу, а спустя некоторое время. Хотелось бы понять, где я напортачил в коде. Код Launcher.exe: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49.
Код ConsoleApplication2.exe: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47.
Буду признателен за конструктивные замечания по теме. Спасибо. ... |
|||
:
Нравится:
Не нравится:
|
|||
06.07.2014, 20:35 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Compositum , Давайте отталкиваться от теории. В мире синхронизации есть классическая структура - монитор Хоара, погуглите. Он состоит из двух фич: 1) Mutual exclusion - когда один поток занял его, другой не может его занять. 2) Conditional variables - когда один поток занял монитор, но внутри критической секции понял, что какое-то условие не выполнено, он может временно отпустить монитор, что бы другой поток его занял, и, возможно, перевел систему в такое состояние, что бы условие первого потока оказалось выполненным, и он смог продолжить работу. Когда второй поток это делает, то перед тем, как отпустить монитор, он сигнализирует другим потокам, что те могут попробовать заново проверить те условия, на которых они обломались. Это самые основы. В чем изъян вашего решения? В том, что вы используете только mutual exclusion, а вам нужна еще и conditional variable. То есть, ваш алгоритм должен выглядеть так: 1) Поток 1 занимает мьютекс. 2) Поток 1 проверяет, что было записано в файл последний раз. 2.1) Если ничего, или же последним писал поток 2 - то записать свою строку. 2.2) Если же последний раз писал этот же поток, то отпустить мьютекс, и ждать, пока второй поток что-то не запишет. Только в такой реализации у вас реально будут чередоваться строки. В вашей же реализации этого условия нет. Поэтому первый поток (процесс) стартует первым, и начинает записывать свою строку много раз. Потом через некоторое время стартует второй поток, и теперь строки начинают чередоваться. Но чередуются они только потому, что захват мьютекса ОС достаточно дорогая операция, а потому как только первый поток отпускает его, второй поток почти всегда успевает захватить его первым. Если вы проделаете тот же трюк, например, с объектом Monitor, или с локальным мьютексом в рамках одного процесса, то вы увидите, что ваше решение не работает не только в начале, но не работает вовсе. Отправная точка вам дана, дальше разбирайтесь сами. Если будет снова непонятно - спрашивайте. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 00:40 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
cdtyjv2) Поток 1 проверяет, что было записано в файл последний раз. 2.1) Если ничего, или же последним писал поток 2 - то записать свою строку. 2.2) Если же последний раз писал этот же поток, то отпустить мьютекс, и ждать, пока второй поток что-то не запишет. Я думал об этом варианте решения в процессе написания кода, но полагал, что мьютекса в данном случае будет достаточно. Мой текущий выбор был обусловлен следующими соображениями: 1. Launcher.exe запускает оба процесса, и они оба моментально "замирают в ожидании", дойдя до строчки кода Код: c# 1.
2. После того, как Launcher.exe освобождает мьютекс Код: c# 1.
Его тут же захватывает любой из ожидающих процессов и выполняет один виток в цикле. В коде этого цикла, сразу после освобождения мьютекса, присутствует строка Код: c# 1.
Её задача - не дать потоку данного процесса сразу пойти на очередной виток (это возможно, т.к. мьютекс освобождён), но вместо этого выполнить переключение исполняемых потоков (в данном случае на поток, выполняемый в др. процессе), при условии что их несколько. Мои предположения о причине проблемы в текущем коде были следующими: 1. Возможно один из процессов не успевает дойти до точки Код: c# 1.
в то время как в коде Launcher.exe мьютекс уже освобождён. Для решения этого я пробовал в Launcher.exe, перед освобождением мьютекса ставить такой код: Код: c# 1.
чтобы оба процесса успели дойти до "нужной кондиции". Однако это не помогло. 2. Возможно метод Код: c# 1.
ориентирован на переключение потоков в рамках одного процесса. P.S. Спасибо за ответ, добавлю в код conditional variable. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 09:15 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Сейчас в голову пришёл третий вариант: 3. Возможно переключение происходит не на второй процесс, как я ожидал, а на Launcher.exe, который замер в ожидании завершения работы процессов. В виду этого тут же происходит очередное переключение, но не на второй процесс, а всё на тот же, первый (т.е. обратно к тому, кто "пнул мячик"). ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 09:19 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
CompositumСейчас в голову пришёл третий вариант: 3. Возможно переключение происходит не на второй процесс, как я ожидал, а на Launcher.exe, который замер в ожидании завершения работы процессов. В виду этого тут же происходит очередное переключение, но не на второй процесс, а всё на тот же, первый (т.е. обратно к тому, кто "пнул мячик"). Переключением процессов управляет виндовс, и произвольным образом. Вы на это повлиять не можете. Кроме того, поведение будет другим, если вы запустите ваши аппликации на многопроцессорной системе. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:04 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Compositumпусть два процесса выполняют запись в один и тот же текстовый файл Это теоретическая или практическая задача? Если практическая, то это не самый удачный вариант. Запись в файл - медленный процесс, и было бы правильнее сделать так, чтобы в файл писал только один поток. Конечно, это не абсолютное утверждение, и в популярных библиотеках логирования есть поддержка записи кучи потоков/процессов в один файл, но это непопулярное решение. Compositumпервый процесс пишет некоторое количество раз слово "dog", а второй - слово "cat". При этом нужно организовать работу процессов так, чтобы слова чередовались Далее, сама по себе идея обеспечения очередности следования потоков противоречит самой идее параллельного программирования CompositumПервое приложение (Launcher.exe) создаёт именованный мьютекс и блокирует его Зачем вообще в первом приложении создавать мьютекс? Что, вторые приложения между собой сами не могут разрулить ситуацию? ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:13 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Arm79Это теоретическая или практическая задача? Это просто "хелло ворлд" для себя, чтобы пощупать на примере. Arm79Зачем вообще в первом приложении создавать мьютекс? Что, вторые приложения между собой сами не могут разрулить ситуацию? Путём введения третьего приложения я планировал подвести оба процесса к "стартовой черте на беговой дорожке", чтобы они находились в ожидании в одной и той же точке кода. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:33 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Ну если только для примера, вот код с упрощениями (не два процесса, а два потока, не файл, а консоль) Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54.
... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:39 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
CompositumПри этом нужно организовать работу процессов так, чтобы слова чередовались Блин, этого не увидел... Сейчас... ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:40 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
CompositumПутём введения третьего приложения я планировал подвести оба процесса к "стартовой черте на беговой дорожке", чтобы они находились в ожидании в одной и той же точке кода. MSMQ может тогда уж лучше юзать? ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:41 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Konst_OneMSMQ может тогда уж лучше юзать? На данный момент я "щупаю" мьютексы. :) ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:44 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Arm79Блин, этого не увидел... А это как раз основное :) ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 11:44 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
CompositumПри этом нужно организовать работу процессов так, чтобы слова чередовались Честно говоря, с мьютексами не получилось. Кажется, такой тип работы через мьютексы реализовать затруднительно. Зато межпроцессное взаимодействие можно организовывать и на основе именованных событий. Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87.
... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 15:20 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Arm79Зато межпроцессное взаимодействие можно организовывать и на основе именованных событий. Спасибо за вариант, правда этот код демонстрирует взаимодействие между потоками в рамках одного и того же процесса, а не межпроцессорное взаимодействие. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 22:39 |
|
Синхронизация потоков через именованные мьютексы
|
|||
---|---|---|---|
#18+
Compositumправда этот код демонстрирует взаимодействие между потоками в рамках одного и того же процесса, а не межпроцессорное взаимодействие. Если бы речь шла о межпотоковом, я бы не заморачивался с p/invoke и вызвал бы конструктор событий нативно. Именованные события как раз для межпроцессорного. К тому же обратите внимание, что в потоках идет получение Event посредством вызова соответствующего API. Если бы речь шла только о потоках, передал бы по ссылке или через поля класса. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.07.2014, 22:52 |
|
|
start [/forum/topic.php?fid=20&fpage=115&tid=1402732]: |
0ms |
get settings: |
10ms |
get forum list: |
13ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
32ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
75ms |
get tp. blocked users: |
1ms |
others: | 42ms |
total: | 193ms |
0 / 0 |