Я много работаю с асинхронными рабочими процессами и агентами в F #, в то время как я немного углублялся в Events. Я заметил, что тип события < _>() не является Thread- безопасно. Здесь я не говорю об общей проблеме поднятия события. Я на самом деле говорю о подписке и удалении/удалении из мероприятия. Для целей тестирования я написал эту короткую программуИспользование события F # в многопоточном коде
let event = Event<int>()
let sub = event.Publish
[<EntryPoint>]
let main argv =
let subscribe sub x = async {
let mutable disposables = []
for i=0 to x do
let dis = Observable.subscribe (fun x -> printf "%d" x) sub
disposables <- dis :: disposables
for dis in disposables do
dis.Dispose()
}
Async.RunSynchronously(async{
let! x = Async.StartChild (subscribe sub 1000)
let! y = Async.StartChild (subscribe sub 1000)
do! x
do! y
event.Trigger 1
do! Async.Sleep 2000
})
0
Программа проста, я создать событие и функцию, которая подписывается определенное количеством событий, к нему, и после этого настраивает каждый обработчик. Я использую другое вычисление async для создания двух экземпляров этих функций с помощью Async.StartChild. После того как обе функции закончены, я запускаю событие, чтобы увидеть, остались ли какие-то обработчики.
Но когда event.Trigger(1)
называется результатом, все еще есть некоторые обработчики, связанные с событием. Поскольку некоторые «1» будут напечатаны на консоли. Это в общем означает, что подписка и/или Disposing не являются потокобезопасными.
И это то, чего я не ожидал. Если подписка и Disposing не являются потокобезопасными, как можно безопасно использовать события в целом? Уверенные события также могут использоваться вне потоков, а триггер не вызывает какую-либо функцию параллельно или на разных потоках. Но для меня как-то нормально, что события используются в коде Async, Agent или вообще с Threads. Они часто используются в качестве сообщения для сбора информации о потоках Backroundworker. С Async.AwaitEvent можно подписаться на событие. Если подписка и Disposing не являются потокобезопасными, как можно использовать события в такой среде? И в чем цель Async.AwaitEvent? Учитывая, что рабочий процесс Async действительно использует Thread, просто используя Async.AwaitEvent, в основном «разбит по дизайну», если подписка/удаление на событие по умолчанию не является потокобезопасной.
Общий вопрос, с которым я столкнулся. Правильно ли, что подписка и утилизация не являются потокобезопасными? Из моего примера это похоже на это, но, вероятно, я пропустил некоторые важные детали. В настоящее время я использую Event много в моем проекте, я обычно имею MailboxProcessors и использую события для уведомления. Так что вопрос. Если события не являются потокобезопасными, весь проект, который я использую в настоящее время, не является потокобезопасным вообще. Так в чем же проблема? Создание целой новой поточной реализации событий? У них уже есть некоторые реализации, которые сталкиваются с этой проблемой? Или есть другие варианты безопасного использования события в среде с высокой степенью потоковой передачи?
Что касается вашего последнего предложения. Каждый асинк начал с Async.Start запускается в пуле потоков, но также использует let! или делать! может переключить асинхронный вызов на другой поток, например, просто используя «Async.Sleep», он переключается на пул потоков или использует Async.StartChild. Наверху я запускаю событие из MailboxProcessor, и они также всегда запускают некоторые потоки в пуле потоков. Поэтому я не вижу, как заставить все работать на одном потоке. Но я тоже не хотел бы этого поведения. Я ожидаю такого поведения, поскольку я все равно не хочу запускать все в одном потоке. –
Похоже, что отсутствие безопасности потоков на событиях является серьезной проблемой для библиотеки ядра F #. Не могли бы вы рассмотреть вопрос об этом? –
Вы можете перевернуть свои собственные сопрограммы или убедиться, что каждый 'let!' 'Do!' Отправляется обратно в «основной поток». Но, как вы сказали, вы не хотите этого поведения, так что это не имеет значения. – FuleSnabel