进度消息的顺序不正确

时间:2017-11-08 10:35:05

标签: c# asynchronous

使用Progress类:

static async Task MyMethodAsync(IProgress<double> progress = null)
{
    int done = 0;
    while (done<100)
    {
        if (progress != null)
            progress.Report(done);
        done++;
    }
}

static async Task CallMyMethodAsync()
{
    var progress = new Progress<double>();
    progress.ProgressChanged += (sender, args) =>
        {
            Console.WriteLine("progr " + args);
        };
    await MyMethodAsync(progress);
}

public static void Main()
{
    CallMyMethodAsync();
    Console.WriteLine("done with caller");
    Console.ReadLine();
}

输出输出不正确:

done with caller
progr 2
progr 3
progr 4
progr 5
progr 6
progr 7
progr 8
progr 9
progr 10
progr 12
progr 11
progr 0
progr 13
progr 16
progr 17
progr 18
progr 19
progr 20
progr 21
progr 22
progr 23
progr 24
progr 25
progr 26

为什么以及如何实现正确的订单?

4 个答案:

答案 0 :(得分:0)

Progress<T>类是异步的。它报告了不同线程的进度,因为它是一个控制台应用程序,正如HashMap所解释的那样:

  

那些没有同步上下文,因此在线程池上提供了进度回调。因此,您不知道他们何时会被安排,并且多个回调可能同时在线,然后竞争控制台锁实际执行他们的写作

您可以自己实现IProgress<T>,这是同步的。使用这个:

public class MyProgress<T> : IProgress<T>
{
    public event ProgressChangedEventHandler<T> ProgressChanged;

    public void Report(T value)
    {
        ProgressChanged?.Invoke(this, value);
    }
}

public delegate void ProgressChangedEventHandler<T>(object sender, T progress);

您只需将您的主叫代码更改为:

var progress = new MyProgress<double>();

答案 1 :(得分:0)

你的意思&#34;阻止并完成......&#34;。

Progress类是异步的。它报告了不同线程的进度,因为它是一个控制台应用程序,正如Damien_The_Unbeliever解释的那样:

这篇文章应该解释为什么你可以'失去&#34;一些电话。

我觉得我的应用程序的细节还很远,但简单的问题是你真的需要为此提供多线程解决方案吗?

如果你真的需要在其他线程和更新UI中执行进程,你可以使用下一个逻辑:

class Class1
{
    public delegate void ProcessProgressEventHandler(int progress);
    public ProcessProgressEventHandler ProcessProgress;
    public EventHandler ProcessEnd;
    public void StartHeavyProcess()
    {
        System.Threading.Thread vThread = new System.Threading.Thread(HeavyProcess);
        ProcessProgress += OnProgress;
        ProcessEnd += OnProcessEnd;
        vThread.Start();
    }

    private void OnProcessEnd(object sender, EventArgs e)
    {
        Console.WriteLine("Process end!");
    }

    private void OnProgress(int progress)
    {
        Console.WriteLine("Progress: " + progress.ToString());
    }

    public void HeavyProcess()
    {
        int done = 0;
        while (done < 100)
        {
            if (ProcessProgress != null)
                ProcessProgress(done);
            done++;
        }
        if (ProcessEnd != null)
            ProcessEnd(this, new EventArgs());
    }
}

P.S。注意,如果你的处理程序调用windows控件moethods你必须在UI线程中调用方法!

答案 2 :(得分:-1)

您获得的结果是因为您使用了启动新线程的任务。

public static void Main()
{
    CallMyMethodAsync();
    Console.WriteLine("done with caller");
    Console.ReadLine();
}

例如第一个命令'CallMyMethodAsync();'只需启动新进程和下一个命令'Console.WriteLine(“使用调用者完成”);'在第一步开始流程结束之前执行。 如果要在第1行开始处理结束后从第2行执行命令,则为:

public static void Main()
{
    CallMyMethodAsync()**.Wait();**
    Console.WriteLine("done with caller");
    Console.ReadLine();
}

答案 3 :(得分:-1)

好的,原因是你使用&#34;事件&#34;但是&#34;事件调度员&#34;不保证按时间顺序处理事件。

我知道的最简单的解决方案是使用委托来告知进度:

static async Task MyMethodAsync(Action<double> progress = null)
{
    int done = 0;
    while (done<100)
    {
        if (progress != null)
            progress.Invoke(done);
        done++;
    }
}