TL; DR:运行任务中的死锁StaTaskScheduler.长版:
我使用的是StaTaskScheduler从ParallelExtensionsExtras中通过平行小组,举办由第三方提供的一些遗留STA COM对象.StaTaskScheduler实现细节的描述如下:
好消息是TPL的实现能够在MTA或STA线程上运行,并考虑到底层API的相关差异,如WaitHandle.WaitAll(当方法提供多个等待句柄时,它只支持MTA线程).
我认为这意味着TPL的阻塞部分将使用等待API来提供消息,例如CoWaitForMultipleHandles,以避免在STA线程上调用时出现死锁情况.
在我的情况下,我相信发生以下情况:进程内STA COM对象A调用进程外对象B,然后期望从B通过回调作为传出调用的一部分.
以简化形式:
var result = await Task.Factory.StartNew(() =>
{
// in-proc object A
var a = new A();
// out-of-proc object B
var b = new B();
// A calls B and B calls back A during the Method call
return a.Method(b);
}, CancellationToken.None, TaskCreationOptions.None, staTaskScheduler);
Run Code Online (Sandbox Code Playgroud)
问题是,a.Method(b)永远不会回来.据我所知,这是因为内部阻塞等待BlockingCollection<Task>不会引发消息,因此我对引用语句的假设可能是错误的.
EDITED相同的代码工作测试WinForms应用程序的UI线程上执行时(即,提供TaskScheduler.FromCurrentSynchronizationContext()的,而不是staTaskScheduler到Task.Factory.StartNew).
解决这个问题的正确方法是什么?我应该实现一个自定义同步上下文,它将显式地使用消息CoWaitForMultipleHandles …
因此,在此之后,我决定在专用的STA线程上显式实例化COM对象.实验表明COM对象需要一个消息泵,我通过调用它来创建Application.Run():
private MyComObj _myComObj;
// Called from Main():
Thread myStaThread = new Thread(() =>
{
_myComObj = new MyComObj();
_myComObj.SomethingHappenedEvent += OnSomthingHappened;
Application.Run();
});
myStaThread.SetApartmentState(ApartmentState.STA);
myStaThread.Start();
Run Code Online (Sandbox Code Playgroud)
如何从其他线程发布STA线程的消息泵消息?
注意: 为了简洁起见,我对问题进行了大量编辑.@Servy的答案的某些部分现在似乎无关紧要,但它们是针对原始问题的.
我正在使用此方法以编程方式实例化Web浏览器,导航到URL并在文档完成时返回结果.
如果文档加载时间超过5秒,我将如何停止Task并GetFinalUrl()返回null?
我见过许多使用a的例子,TaskFactory但我无法将其应用于此代码.
private Uri GetFinalUrl(PortalMerchant portalMerchant)
{
SetBrowserFeatureControl();
Uri finalUri = null;
if (string.IsNullOrEmpty(portalMerchant.Url))
{
return null;
}
Uri trackingUrl = new Uri(portalMerchant.Url);
var task = MessageLoopWorker.Run(DoWorkAsync, trackingUrl);
task.Wait();
if (!String.IsNullOrEmpty(task.Result.ToString()))
{
return new Uri(task.Result.ToString());
}
else
{
throw new Exception("Parsing Failed");
}
}
// by Noseratio - http://stackoverflow.com/users/1768303/noseratio
static async Task<object> DoWorkAsync(object[] args)
{
_threadCount++;
Console.WriteLine("Thread count:" + _threadCount);
Uri retVal = null;
var wb = new WebBrowser();
wb.ScriptErrorsSuppressed = …Run Code Online (Sandbox Code Playgroud) .net c# console-application webbrowser-control task-parallel-library
是否有一个同步等待函数不会占用.NET WPF中的UI线程?就像是:
Sub OnClick(sender As Object, e As MouseEventArgs) Handles button1.Click
Wait(2000)
'Ui still processes other events here
MessageBox.Show("Is has been 2 seconds since you clicked the button!")
End Sub
Run Code Online (Sandbox Code Playgroud) 有人给我发了电子邮件,询问我是否有WaitOneAndPumpWPF 的版本.目标是WaitHandle.WaitOne在同一堆栈帧上等待句柄(类似于)并在等待时泵送WPF Dispatcher事件.
我真的不认为这样的API应该用于任何生产代码,无论是WinForms还是WPF(可能除了UI自动化之外).WPF没有暴露WinForms的显式版本DoEvents,这是一个非常好的设计决策,考虑到DoEventsAPI一直在滥用的公平份额.
然而,这个问题本身很有意思,所以我将把它作为一个练习并发布任何我想出的答案.如果有兴趣,请随意发布您自己的版本.
我知道没有必要等待,Parallel.For但是因为在Parallel.For我想等待Parallel.For完成期间正在处理 UI Windows 消息(WinForms 消息泵)。
将 a 封装在 aParallel.For内Task,然后等待它是个好主意吗?有没有更好的办法?
谢谢。
CancellationTokenSource token = new CancellationTokenSource();
const int len = 100;
double[] array = new double[len];
Task t = Task.Factory.StartNew(delegate {
Parallel.For(0, len, delegate(int i, ParallelLoopState loopState) {
array[i] += 1;
});
try
{
t.Wait(token.Token);
}
catch (OperationCanceledException e)
{
rc = false;
}
Run Code Online (Sandbox Code Playgroud) 我想等待WebBrowser控件完成导航.所以我创建一个事件,然后我想等待它被设置:
procedure TContoso.NavigateToEmpty(WebBrowser: IWebBrowser2);
begin
FEvent.ResetEvent;
WebBrowser.Navigate2('about:blank'); //Event is signalled in the DocumentComplete event
Self.WaitFor;
end;
Run Code Online (Sandbox Code Playgroud)
然后我在事件中设置DocumentComplete事件:
procedure TContoso.DocumentComplete(ASender: TObject; const pDisp: IDispatch; const URL: OleVariant);
var
doc: IHTMLDocument2;
begin
if (pDisp <> FWebBrowser.DefaultInterface) then
begin
//This DocumentComplete event is for another frame
Exit;
end;
//Set the event that it's complete
FEvent.SetEvent;
end;
Run Code Online (Sandbox Code Playgroud)
问题在于如何等待此事件发生.
第一反应是等待事件被触发:
procedure TContoso.WaitFor;
begin
FEvent.WaitFor;
end;
Run Code Online (Sandbox Code Playgroud)
问题在于DocumentComplete事件永远不会触发,因为应用程序永远不会空闲,以允许COM事件通过.
我的第一反应是忙着睡觉,等待一面旗帜:
procedure TContoso.NavigateToEmpty(WebBrowser: IWebBrowser2);
begin
FIsDocumentComplete := …Run Code Online (Sandbox Code Playgroud)