【问题标题】:Call method on UI thread from background in .net 2.0从.net 2.0的后台调用UI线程上的方法
【发布时间】:2013-02-21 08:48:57
【问题描述】:

我正在使用 MonoDevelop (.net 2.0) 开发 iOS 和 Android 应用程序。我使用 BeginGetResponse 和 EndGetResponse 在后台线程中异步执行 webrequest。

IAsyncResult result = request.BeginGetResponse(new AsyncCallback(onLogin), state);

但是,回调onLogin 似乎仍在后台线程上运行,不允许我与 UI 交互。我该如何解决?

可以看到有 Android 和 iOS 特定的解决方案,但想要一个跨平台的解决方案。

编辑:从 mhutch 的回答中我得到了这么多:

IAsyncResult result = request.BeginGetResponse(o => {
            state.context.Post(() => { onLogin(o); });
        }, state);

其中 state 包含一个 context 类型的变量 SynchronizationContext 设置为 SynchronizationContext.Current

它抱怨 Post 需要两个参数,第二个是 Object state。插入state 会出现错误

Argument `#1' cannot convert `anonymous method' expression to type `System.Threading.SendOrPostCallback' (CS1503) (Core.Droid)

【问题讨论】:

    标签: c# multithreading user-interface .net-2.0 monodevelop


    【解决方案1】:

    Xamarin.iOS 和 Xamarin.Android 都为 GUI 线程设置了 SynchronizationContext

    这意味着您从 GUI 线程获取 SynchronizationContext.Current 并将其传递给您的回调(例如,通过状态对象或在 lambda 中捕获)。然后就可以使用上下文的Post 方法调用主线程上的东西了。

    例如:

    //don't inline this into the callback, we need to get it from the GUI thread
    var ctx = SynchronizationContext.Current;
    
    IAsyncResult result = request.BeginGetResponse(o => {
        // calculate stuff on the background thread
        var loginInfo = GetLoginInfo (o);
        // send it to the GUI thread
        ctx.Post (_ => { ShowInGui (loginInfo); }, null);
    }, state);
    

    【讨论】:

    • 感谢您的回复。看起来这就是我想要的。你能给我一个例子,其中上下文是用statevariable 传递的。现在它抱怨找不到 GetLoginInfo。而ShowInGui我假设你的意思是onLogin
    • 对,GetLoginInfo 和 ShowInGui 只是代表您希望在后台线程中运行的代码和您希望在 GUI 线程中运行的代码的示例。
    • 通过状态对象传递上下文很容易。您可以将任何状态对象传递给 BeginGetResponse,并且您的回调可以从 IAsyncResult 的 AsyncState 属性中获取它。因此,您可以通过这种方式将数据传递给您的回调。这是引入匿名委托和 lambda 之前的旧模式。
    • System.Threading.SynchronizationContext.Post 有参数 System.Threading.SendOrPostCallback 和对象。我将状态作为第二个参数添加到 Post 但它无法将“匿名方法”表达式转换为类型“System.Threading.SendOrPostCallback”。
    • 好吧,我的例子有点不对,Post 也接受了某种状态对象并将其传递给回调。修复了它,将 null 作为状态对象传递,并使用 _ 声明带有忽略参数的 lambda。尽管在您的特定情况下,您可能会将onLogin 的参数更改为object 类型,执行:state.context.Post(onLogin, o);,并且Post 将通过oonLogin
    【解决方案2】:

    我不确定这是否适用于 Mono,但我通常在 WinForm 应用程序上执行此操作。假设您要执行方法X()。那么:

    public void ResponseFinished() {
        InvokeSafe(() => X()); //Instead of just X();
    }
    
    public void InvokeSafe(MethodInvoker m) {
        if (InvokeRequired) {
            BeginInvoke(m);
        } else {
            m.Invoke();
        }
    }
    

    当然,这是在 Form 类中。

    【讨论】:

    • 表单类?我应该导入什么? “使用 System.Threading”没有帮助。它可以找到 MethodInvoker、InvokeRequired、BeginInvoke 或 Invoke 中的任何一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多