F# Async.AwaitEvent и события взаимодействия COM

У меня есть COM-объект, к которому я подключаюсь, и я должен получить событие, которое подтвердит, что соединение установлено. Я пишу код и тестирую его в F# интерактивно, и по какой-то причине он не будет перехватывать события COM, когда я использую Async.RunSynchronously,

/// This class wraps COM event into F# Async-compatible event
type EikonWatcher(eikon : EikonDesktopDataAPI) =
    let changed = new Event<_>()
    do eikon.add_OnStatusChanged (fun e -> changed.Trigger true)
    member self.StatusChanged = changed.Publish

/// My method
let ``will that connection work?`` () = 
    let eikon = EikonDesktopDataAPIClass() :> EikonDesktopDataAPI // create COM object
    let a = async {
        let watcher = EikonWatcher eikon // wrap it 
        eikon.Initialize() |> ignore     // send connection request
        let! result =  Async.AwaitEvent watcher.StatusChanged // waiting event
        printfn "%A" result              // printing result
        return result
    }

    // I use either first or second line of code, not both of them
    Async.Start (Async.Ignore a)            // does not hang, result prints 
    Async.RunSynchronously (Async.Ignore) a // hangs!!!


/// Running
``will that connection work?`` ()

В то же время код прекрасно работает с RunSynchronously когда я вставляю его в консольное приложение. Что я должен сделать, чтобы предотвратить это противное поведение?

1 ответ

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

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

Когда вы указываете Async.Start он запускает новое, независимое вычисление, которое выполняется само по себе, "поток".

Принимая во внимание, что при вызове выполняется синхронно, он ожидает в том же "потоке".

Теперь, если событие, которое вы ожидаете и которое кажется независимым, на самом деле "линеаризуется" тем же циклом событий, вы фактически ждете себя, отсюда и тупик.


Что-то полезное, если вы хотите ждать "асинхронно" (то есть ждать события, но на самом деле не блокировать и не оставлять возможность для выполнения какой-либо другой задачи), вы можете использовать следующий код в вашем асинхронном блоке:

async {
         ....
         let! token = myAsyncTask |> Async.StartChild
         let! result = token
         ....  
}
Другие вопросы по тегам