当控制在新创建的线程上调用正确的线程方法() [英] Calling method on correct thread when control is created in new Thread()

查看:158
本文介绍了当控制在新创建的线程上调用正确的线程方法()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个新的 web浏览器()在一个新的发控制()

这个问题我有,是调用委托我的 web浏览器时,主线的电话,电话将出现在的主线程。我希望这样的事情发生在 browserThread

 私有静态web浏览器defaultApiClient = NULL;

委托无效DocumentNavigator(字符串URL);


私人的WebAPI(){

    //创建负责一个新的线程
    //对于进行API调用。
    螺纹browserThread =新主题(()=> {

        defaultApiClient =新的web浏览器();

        //设置我们的代表
        documentNavigatorDelegate =新DocumentNavigator(defaultApiClient.Navigate);

        //匿名事件处理程序
        defaultApiClient.DocumentCompleted + =(对象发件人,WebBrowserDocumentCompletedEventArgs E)=> {
            //做杂项。事
        };

        Application.Run();
    });
    browserThread.SetApartmentState(ApartmentState.STA);
    browserThread.Start();

}

DocumentNavigator documentNavigatorDelegate = NULL;
私人无效EnsureInitialized(){

    //这个总是返回假出于某种原因
    如果(defaultApiClient.InvokeRequired){

        //如果我直接跳到这个调用
        //装上System.Windows.Forms.dll的一个破发点!System.Windows.Forms.WebBrowser.Navigate(字符串urlString,串targetFrameName,byte []的POSTDATA,串additionalHeaders)
        //我发现,我的电话是在主线程正在做。我希望这是在做browserThread,而不是
        对象result = defaultApiClient.Invoke(documentNavigatorDelegate,WebApiUrl);

    }

}
 

我试图调用方法的方式万千:

  //呼吁主线程(预期)
defaultApiClient.Navigate(WebApiUrl);

//呼吁主线程
defaultApiClient.Invoke(documentNavigatorDelegate,WebApiUrl);

//呼吁主线程
defaultApiClient.BeginInvoke(documentNavigatorDelegate,WebApiUrl);

//呼吁主线程
documentNavigatorDelegate.Invoke(WebApiUrl);

//随机工作者线程调用
documentNavigatorDelegate.BeginInvoke(WebApiUrl,新的AsyncCallback((IAsyncResult的结果)=> {....}),NULL);
 

更新

让我打破我的最终目标,一点点地使事情变得更加清晰:的我必须使用拨打电话 WebBrowser.Document.InvokeScript(),但是文件未加载我打电话后,才 WebBrowser.Navigate(),然后 WebBrowser.DocumentComplete 事件触发。从本质上讲,我不能让我的预期调用 InvokeScript()后才的DocumentComplete 火灾......我想等待对文档加载(阻塞手机号码),所以我可以叫 InvokeScript ,并以同步的方式回到我的结果。

基本上,我需要等待我的文件来完成,我想做到这一点的方法是使用的AutoResetEvent()类,我将在一个DocumentComplete触发被解雇......我需要所有这些东西发生在一个单独的线程。

我看到的另一种选择是做什么的喜欢的这样的:

 私人BOOL初始化= FALSE;
私人无效EnsureInitialized(){
    defaultApiClient.Navigate(WebApiUrl);
    而(!初始化){
        Thread.sleep代码(1000); //这个块,这样在技术上是行不通的
    }
}

私人无效defaultApiClient_DocumentComplete(对象发件人,WebBrowserDocumentCompletedEventArgs E){
    初始化= TRUE;
}
 

解决方案

这是设计使然。控制的InvokeRequired /的BeginInvoke / Invoke的成员需要的处理的要创建的控件的属性。这是它可以找出哪些特定的线程调用的主要方式。

但是,这并没有发生在你的code,手柄通常只有当你添加一个控制父母的控件集合创建和家长被显示展()。换句话说,实际上创建的主机窗口的浏览器。没有这一切都发生在你的code所以处理还是IntPtr.Zero和InvokeRequired返回的的。

这是不可以实际上是一个问题。 web浏览器类是特殊的,它是引擎盖下的COM服务器。 COM手柄线程离开它由程序员,从.NET的工作方式非常不同的细节本身,而不是。它会自动封送调用它的导航()方法。这是完全自动的,不需要任何帮助。好客家为COM服务器是所有的需要,通过创建一个STA线程和抽水用Application.Run消息循环()做了一个。这是COM用来做自动编组的消息循环。

因此​​,你可以简单地调用导航()在你的主线程并没有什么不顺心。该DocumentCompleted事件仍会触发辅助线程上,你可以把你的快乐的时间上线文档修修补补。

不知道为什么所有的这是一个问题,它应该工作的所有的只是罚款。也许你只是困惑不解其行为。如果没有,那么<一个href="http://stackoverflow.com/questions/21680738/how-to-post-messages-to-an-sta-thread-running-a-message-pump/21684059#21684059">this回答可以帮助你用更通用的解决方案。不要害怕拒绝,塞耶斯太多顺便说一句,在工作线程显示用户界面充满了陷阱,但你从来没有真正显示任何UI在这里,不要再创建一个窗口。

I've created a new WebBrowser() control in a new Thread().

The problem I'm having, is that when invoking a delegate for my WebBrowser from the Main Thread, the call is occurring on the Main Thread. I would expect this to happen on browserThread.

private static WebBrowser defaultApiClient = null;

delegate void DocumentNavigator(string url);


private WebApi() {

    // Create a new thread responsible 
    // for making API calls.
    Thread browserThread = new Thread(() => {

        defaultApiClient = new WebBrowser();

        // Setup our delegates
        documentNavigatorDelegate = new DocumentNavigator(defaultApiClient.Navigate);

        // Anonymous event handler
        defaultApiClient.DocumentCompleted += (object sender, WebBrowserDocumentCompletedEventArgs e) => {
            // Do misc. things
        };

        Application.Run();
    });
    browserThread.SetApartmentState(ApartmentState.STA);
    browserThread.Start();

}

DocumentNavigator documentNavigatorDelegate = null;
private void EnsureInitialized() {

    // This always returns "false" for some reason
    if (defaultApiClient.InvokeRequired) {

        // If I jump ahead to this call
        // and put a break point on System.Windows.Forms.dll!System.Windows.Forms.WebBrowser.Navigate(string urlString, string targetFrameName, byte[] postData, string additionalHeaders)
        // I find that my call is being done in the "Main Thread".. I would expect this to be done in "browserThread" instead
        object result = defaultApiClient.Invoke(documentNavigatorDelegate, WebApiUrl);

    }

}

I've tried invoking the method a myriad of ways:

// Calls on Main Thread (as expected)
defaultApiClient.Navigate(WebApiUrl);

// Calls on Main Thread
defaultApiClient.Invoke(documentNavigatorDelegate, WebApiUrl); 

// Calls on Main Thread
defaultApiClient.BeginInvoke(documentNavigatorDelegate, WebApiUrl); 

// Calls on Main Thread
documentNavigatorDelegate.Invoke(WebApiUrl);

// Calls on random Worker Thread
documentNavigatorDelegate.BeginInvoke(WebApiUrl, new AsyncCallback((IAsyncResult result) => { .... }), null);

Update

Let me break down my end-goal a little bit to make things more clear: I have to make calls using WebBrowser.Document.InvokeScript(), however Document is not loaded until after I call WebBrowser.Navigate() and THEN the WebBrowser.DocumentComplete event fires. Essentially, I cannot make my intended call to InvokeScript() until after DocumentComplete fires... I would like to WAIT for the document to load (blocking my caller) so I can call InvokeScript and return my result in a synchronous fashion.

Basically I need to wait for my document to complete and the way I would like to do that is with a AutoResetEvent() class which I will trigger upon DocumentComplete being fired... and I need all this stuff to happen in a separate thread.

The other option I see is doing something like this:

private bool initialized = false;
private void EnsureInitialized(){
    defaultApiClient.Navigate(WebApiUrl);
    while(!initialized){
        Thread.Sleep(1000); // This blocks so technically wouldn't work
    }
}

private void defaultApiClient_DocumentComplete(object sender, WebBrowserDocumentCompletedEventArgs e){
    initialized = true;
}

解决方案

This is by design. The InvokeRequired/BeginInvoke/Invoke members of a control require the Handle property of the control to be created. That is the primary way by which it can figure out to what specific thread to invoke to.

But that did not happen in your code, the Handle is normally only created when you add a control to a parent's Controls collection and the parent was displayed with Show(). In other words, actually created the host window for the browser. None of this happened in your code so Handle is still IntPtr.Zero and InvokeRequired returns false.

This is not actually a problem. The WebBrowser class is special, it is a COM server under the hood. COM handles threading details itself instead of leaving it up to the programmer, very different from the way .NET works. And it will automatically marshal a call to its Navigate() method. This is entirely automatic and doesn't require any help. A hospitable home for the COM server is all that's needed, you made one by creating an STA thread and pumping a message loop with Application.Run(). It is the message loop that COM uses to do the automatic marshaling.

So you can simply call Navigate() on your main thread and nothing goes wrong. The DocumentCompleted event still fires on the helper thread and you can take your merry time tinkering with the Document on that thread.

Not sure why any of this is a problem, it should work all just fine. Maybe you were just mystified about its behavior. If not then this answer could help you with a more universal solution. Don't fear the nay-sayers too much btw, displaying UI on a worker thread is filled with traps but you never actually display any UI here and never create a window.

这篇关于当控制在新创建的线程上调用正确的线程方法()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆