none
求助啊 线程同步问题 RRS feed

  • 问题

  • 问题是这样的 

    为了实验线程同步,有这么一段代码:

    Task t = new Task(async () => {
                    await Fun1(null);
                    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
                });
                t.Start();
                t.ContinueWith(async (task) =>
                {
                    await Fun1(null);
                    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
                });
    
    
    public async Task  Fun1(Object xf)
            {
                System.Diagnostics.Debug.WriteLine("fun1 begin."+Task.CurrentId);
                await Fun2();
                System.Diagnostics.Debug.WriteLine("after fun2." + Task.CurrentId);
            }
    
            public async Task Fun2()
            {
                System.Diagnostics.Debug.WriteLine("fun2 begin." + Task.CurrentId);
                await Fun3();
                System.Diagnostics.Debug.WriteLine("after fun3." + Task.CurrentId);
            }
            public async Task Fun3()
            {
                System.Diagnostics.Debug.WriteLine("fun3 begin." + Task.CurrentId);
                Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
                {
                    System.Diagnostics.Debug.WriteLine("Dispatcher3" + Task.CurrentId);
                }).AsTask();
                await Fun4();
                System.Diagnostics.Debug.WriteLine("after fun4." + Task.CurrentId);
                
            }
            public async Task Fun4()
            {
                Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
                {
                    System.Diagnostics.Debug.WriteLine("Dispatcher4" + Task.CurrentId);
                }).AsTask();
                Dispatcher.ProcessEvents(Windows.UI.Core.CoreProcessEventsOption.ProcessAllIfPresent);
                System.Diagnostics.Debug.WriteLine("fun4 begin." + Task.CurrentId);
                return;
            }

    输出结果是:

    fun1 begin.1
    fun2 begin.1
    fun3 begin.1
    Dispatcher3
    Dispatcher4
    fun4 begin.1
    after fun4.1
    after fun3.1
    after fun2.1
    after fun1.1
    fun1 begin.2
    fun2 begin.2
    fun3 begin.2
    Dispatcher3
    fun4 begin.2
    after fun4.2
    Dispatcher4
    after fun3.2
    after fun2.2
    after fun1.2

    这样是没有问题的 。

    如果我把FUN3 和FUN4的Dispatcher改为:

    public async Task Fun3()
            {
                System.Diagnostics.Debug.WriteLine("fun3 begin." + Task.CurrentId);
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
                {
                    System.Diagnostics.Debug.WriteLine("Dispatcher3" + Task.CurrentId);
                }).AsTask();
                await Fun4();
                System.Diagnostics.Debug.WriteLine("after fun4." + Task.CurrentId);
                
            }
            public async Task Fun4()
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
                {
                    System.Diagnostics.Debug.WriteLine("Dispatcher4" + Task.CurrentId);
                }).AsTask();
                Dispatcher.ProcessEvents(Windows.UI.Core.CoreProcessEventsOption.ProcessAllIfPresent);
                System.Diagnostics.Debug.WriteLine("fun4 begin." + Task.CurrentId);
                return;
            }

    就是在Dispatcher前加了一个AWAIT,结果就变成了:

    fun1 begin.1
    fun2 begin.1
    fun3 begin.1
    Dispatcher3
    Dispatcher4
    fun4 begin.1
    after fun4.1
    after fun3.1
    after fun2.1
    after fun1.1
    fun1 begin.2
    fun2 begin.2
    fun3 begin.2
    Dispatcher3
    Dispatcher4
    fun4 begin.
    after fun4.
    after fun3.
    after fun2.
    after fun1.

    在第二个Task的时候await 了一下 Dispatcher ,打出当前线程ID发现为空。

     在实际应用中也发现了调用await Dispatcher会导致当前线程提前结束,await之后线程ID打出来都是空值

    这结果就不同步了啊 这是为什么啊 求解啊 诸位高手

     

     

     

     


    2012年9月25日 5:04

答案

  • 我确实能够重现你的这个现象,读不出CurrentId 在await Dispacther之后。但是我不是很清楚你所谓的不同步ObservableCollection是一个什么概念。

    从你第一个Post的代码来看,你的Task.ContinueWith的回调是async的,所以我的建议是你在回调中等待你的之前的task完成:

                t.ContinueWith(async (task) =>
                {
                    await task;
                    await Fun1(null);
                    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
                });


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年9月26日 8:11
    版主

全部回复

  • 其实就是我的XAML上的GRID绑定了一个

    ObservableCollection

    集合,我需要在后台用线程更新这个集合。 如果不用Dispatcher的话就会出现跨线程调用异常。如果使用了await Dispatcher的话,又会产生线程不同步的问题 。这该如何是好啊 搞得我快疯掉了

    求高人指点啊 小弟感激不尽

    2012年9月25日 5:11
  • 我确实能够重现你的这个现象,读不出CurrentId 在await Dispacther之后。但是我不是很清楚你所谓的不同步ObservableCollection是一个什么概念。

    从你第一个Post的代码来看,你的Task.ContinueWith的回调是async的,所以我的建议是你在回调中等待你的之前的task完成:

                t.ContinueWith(async (task) =>
                {
                    await task;
                    await Fun1(null);
                    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
                });


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年9月26日 8:11
    版主
  • Thanks Bob哥

    还有2个问题啊 ~

    1.

     t.ContinueWith(async (task) => {
                    await task
    ;
                    await
    Fun1(null);
                   
    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
               
    });

    await  t.ContinueWith(async (task) => {
                    await
    Fun1(null);
                   
    System.Diagnostics.Debug.WriteLine("after fun1." + Task.CurrentId);
               
    });

    是不是等效的哦?

    2.

    关于TASK取消的。

    CancellationToken tokenCancel 是不是要传递进线程体,并且在每一个

    await耗时操作之前都要判断

    if (tokenCancel.IsCancellationRequested)
                {
                    tokenCancel.ThrowIfCancellationRequested();
                }
    然后在外边用CATCH()捕获哦?

    其实就是我有个需求:在TASK中往数据集合里添加数据,在收到指令后取消TASK,并清空数据集合。现在碰到个问题就是:我发送了CANCEL指令后貌似线程还在运行一段时间,我在发送CANCEL指令后立即清空数据,这样可能会发现清空数据了之后还有少量数据往里添加。 于是我就按照上边的做法把CancellationToken 传递进线程体,并在每一个耗时操作之前判断的方法来处理的。不知道这种处理方式是否正确或者说有没有更好的一种做法呢?

    2012年9月27日 1:04