同步上下文的SynchronizationContext.Post到UI方法

7
我正在处理与网络服务相关的工作,因此需要延长会话长度/重新连接并获取大量数据集等。有时这可能需要较长时间,因此我希望在单独的线程中进行操作,以便异步更新UI。
我似乎无法理解如何使用同步上下文在我的UI线程上调用方法。我已经将我的UIThread上下文传递给了我的线程,现在我想在UI线程上更新一些标签等。我已经阅读了大量文章,但没有一个能够简单地解释如何将一些参数传回方法,或者可能是因为我太累/愚蠢而没有看到。
//在主UI线程上
public void updateConnStatus(string conn_name, bool connected)
{
        switch (conn_name)
        {
            case "Conn" : if (connected == true){ //do something} break;

//在单独的线程中执行

uiContext.Post( //something to do with delegates in here that eludes me );

如果有人能简单地解释一下我如何将sendOrPostCallBack链接到原始方法,我会非常感激。

谢谢

编辑:

我设法使代码运行并尝试触发事件,它可以正确填充我的自定义eventArgs,但是它要么表示updateUIConnStatus未实例化,需要更多调查:o

public void updateUIThread(string conn, bool connected)
    {
       uiContext.Post(new SendOrPostCallback((o) => { updateConnStatus(this, new MyEventArgs<String, Boolean>(conn, connected)); }), null);
    }

public class MyEventArgs<T, U> : EventArgs
    {
        private T _val1; private U _val2;
        public  MyEventArgs(T value1, U value2) { _val1 = value1; _val2 = value2; }
        public T val1 { get { return _val1;} }
        public U val2 { get {return _val2;} }
    }

public event EventHandler<MyEventArgs<String, Boolean>> updateConnStatus = Delegate {};

//现在在UI线程

 public void updateConnStatus(object sender, MyEventArgs<String,Boolean> e)
    {
        switch (e.val1)
        {
            case "Conn1" :
                if (e.val2 == true)
                {
2个回答

21

你需要一个类型为SendOrPostCallback的委托。这相当麻烦,因为它只接受一个对象类型的参数。你肯定应该看一下在.NET 4中可用的Task<>类来使这个过程更容易。或者像这样使用lambda表达式:

        string conn_name = "foo";
        uiContext.Post(new SendOrPostCallback((o) => {
            updateConnStatus(conn_name, true);
        }), null);

{大括号中的代码}在UI线程上执行。


嗨,汉斯,我最初尝试了类似的东西,但由于代码在线程包装器类中,我收到了“当前上下文中不存在'updateConnStatus'名称”的错误。我刚刚尝试了在那里添加事件处理程序,这似乎已经解决了错误,所以我会测试一下并回来! :) - Peter Lea
好的,如果该方法位于另一个类中,请使用正确的对象引用。或者可以考虑使用事件。 - Hans Passant
不,我没有正确使用它 :( 我有在vb.net中使用委托和AddressOf & invoke等的工作示例,但我对c#还很陌生,看起来SynchonisationContext是一种更好的方法,但我似乎无法正确生成事件处理程序或引用主UI方法 :/ - Peter Lea
好的,迈出小步。不要使用“线程包装器”,只需将其放在与updateConnStatus方法相同的类中。学习面向对象编程和忘却vb.net需要一些时间。 - Hans Passant
我成功地通过使用泛型创建自定义的EventArgs类来使代码运行起来。只是似乎事件存在一些问题。 - Peter Lea
显示剩余3条评论

7

通常您在UI线程上创建类型的实例(例如ViewModels),因此只需将SynchronizationContext或TaskScheduler(我个人推荐)保存到私有字段中,然后在需要时进行比较即可...

private readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
private readonly TaskScheduler _scheduler = TaskScheduler.Current;

void OnSomeEvent(object sender, EventArgs e)
{
    if (_syncContext != SynchronizationContext.Current)
    {
        // Use Send if you need to get something done as soon as possible.
        // We'll be polite by using Post to wait our turn in the queue.
        _syncContext.Post(o => DoSomething(), null);
        return;
    }
    // Call directly if we are already on the UI thread
    DoSomething();
}

void OnSomeOtherEvent(object sender, MyEventArgs e)
{
    var arg1 = e.Arg1; // "Hello "
    var arg2 = e.Arg2; // {"World", "!"};

    // Process args in the background, and then show the result to the user...
    // NOTE: We don't even need to check the context because we are passing
    // the appropriate scheduler to the continuation that shows a MessageBox.

    Task<string>.Factory.StartNew(() => ReturnSomething(arg1, arg2))
        .ContinueWith(t => MessageBox.Show(t.Result), _scheduler);
}

void DoSomething() { MessageBox.Show("Hello World!"); }

string ReturnSomething(string s, IEnumerable<string> list)
{
    return s + list.Aggregate((c, n) => c + n);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接