Как синхронизировать возврат при использовании ThreadPool.QueueUserWorkItem(Новый WaitCallback для создания канала?

Мне нужно создать WCF ChannelFactory в отдельном потоке. Проблема, с которой я сталкиваюсь сейчас, заключается в том, что переменная svc не всегда возвращает значение:

   ...
   Dim svc As T = Nothing
   Dim svcft As New DuplexChannelFactory(Of T)(caller, ep)
   ThreadPool.QueueUserWorkItem(New WaitCallback(Sub(obj) svc = svcft.CreateChannel()))
   ...

Большую часть времени svc вернет нулевое значение, но иногда возвращает хорошую ссылку. Что я делаю неправильно?

Я изменил свой код в соответствии с комментарием YK1, как показано ниже. это все еще не решает проблему - svc все еще не всегда устанавливает:

Task.Factory.StartNew(
    Sub()
        svc = svcft.CreateChannel()
    End Sub
).ContinueWith(
    Sub()
        svcft = Nothing
    End Sub
)
If svc Is Nothing Then
    Throw New Exception("Creating service reference failed.") '<== get the error here...
End If

1 ответ

Решение

Ваша проблема в состоянии гонки. Строка после ThreadPool.QueueUserWorkItem может иметь или не иметь правильное значение для svc в зависимости от того, как запланирован текущий поток и поток пула потоков. Это плохо, и мы не хотим делать это таким образом.

ThreadPool.QueueUserWorkItem это старый способ запуска вещей в потоках потоков в огне и забывчивости. Обычно вы не хотите синхронизировать данные оттуда (хотя вы можете использовать eventsоднако это все блокирующие пути).

Мы больше не хотим блокировать потоки приложений. Итак, приходит Async/Await а также TPL,

Вот как вы можете это сделать:

Async Function MyFuncAsync(Of T)() As Task
   ...
   Dim svc As T = Nothing
   Dim svcft As New DuplexChannelFactory(Of T)(caller, ep)
   svc = Await Task.Run(Funtion() svcft.CreateChannel())
   ...
End Function

Если вы не на.NET 4.5 и не хотите использовать async/await ты можешь использовать TPL API напрямую.

...
Dim svc As T = Nothing
Dim svcft As New DuplexChannelFactory(Of T)(caller, ep)
Task.Factory.StartNew(
    Sub() 
        svc = svcft.CreateChannel()
    End Sub
).ContinueWith(
    Sub(t) 
        `rest of code here
    End Sub
)

Если ваш основной поток является потоком пользовательского интерфейса (WPF/Winforms), если вы хотите выполнить синхронизацию обратно в потоке пользовательского интерфейса, то ваше продолжение может быть выполнено для выполнения в потоке пользовательского интерфейса. (async/await выше захвата syncdhronizationcontext автоматически).

...
Dim svc As T = Nothing
Dim svcft As New DuplexChannelFactory(Of T)(caller, ep)
Task.Factory.StartNew(
    Sub() 
        svc = svcft.CreateChannel()
    End Sub
).ContinueWith(
    Sub(t) 
        `rest of code here
    End Sub,
TaskScheduler.FromCurrentSynchronizationContext())
Другие вопросы по тегам