Как синхронизировать возврат при использовании 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())