使用ThreadPool.QueueUserWorkItem时如何同步返回(New WaitCallback创建通道?

时间:2013-07-23 19:36:56

标签: vb.net multithreading wcf sync

我需要在单独的线程上创建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 个答案:

答案 0 :(得分:1)

你的问题是竞争条件。 ThreadPool.QueueUserWorkItem之后的行可能会或可能不具有svc的正确值,具体取决于当前线程和线程池线程的调度方式。这很糟糕,我们不想这样做。

ThreadPool.QueueUserWorkItem是以火和忘记的方式在线程池线程上运行东西的旧方法。您通常不希望从那里同步(尽管您可以使用events,但这些都是阻止方式)。

我们不想再阻止应用程序线程了。因此,请Async/AwaitTPL

以下是您可以这样做的方式:

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
)

如果你的主线程是UI线程(WPF / Winforms),如果你想在UI线程上同步,那么你可以继续在UI线程上运行。 (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())