zoukankan      html  css  js  c++  java
  • # C#之异步

    在异步程序中,程序代码不需要按编写时的顺序严格执行。有时需要在一个新的线程中运行一部分代码,有时无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序,从而解决在性能或用户体验上导致的难以接受的行为。

    在C#5.0引入的一个来用来构建异步方法的新特性---async/await,但还有其他形式的异步编程的特性,这些特性是.NET框架的一部分,但没有嵌入C#语言,比如BackgroundWorker类和.NET任务并行库,两者均通过新建线程来实现异步。

    async/await特性的结构

    异步的方法在处理完之前就返回到调用方法。C#的async/await特性可以创建并使用异步方法。该特性有三部分组成。

    • 调用方法:该方法调用异步方法,然后在异步方法(可能在相同的线程,也可能在不同的线程)执行其任务的时候继续执行。
    • 异步方法,该方法异步执行其工作,然后立即返回到调用方法。
    • await表达式:用于异步方法内部,指明需要异步执行的任务,一个异步方法可以包含任意多个await表达式,不过如果一个都不包含的话编译器会发出警告

    什么是异步方法

    在语法上,异步方法具有如下特点:

    • 方法头中包含async方法修饰,且必须出现在返回类型之前,async关键字是一个上下文关键字,它只是标识该方法包含了一个或多个await表达式,也就是说async本身并不能创建任何异步操作。
    • 方法中包含一个或多个await表达式,用以表示可以异步完成的任务
    • 返回类型只有三种:Task<T>(该类型,异步方法必须有相应的return T obj与之呼应);Task(该方法不能有return 语句),且在调用方法控制流中可以设置task.Wait()方法来等待异步方法结束,如果没有的话,调用方法的控制流(主程序)结束 后,异步方法将被迫结束。
    • 异步方法的参数可以为任意数量的任意类型,但不能为out或ref参数
    • 按照约定,异步方法的名称应以Async为后缀。
    • 除了方法外,Lambda表达式和匿名方法也可以作为异步的对象.

    (1)返回类型是Task<T>的异步方法

    任何返回Task类型的异步方法的返回值,必须为T类型或可以隐式转换为T的类型。

    namespace ConsoleApp
    {
        static class DoStuff
        {
            public  static async  Task<int> FindSeriesSum() //返回类型是Task<int>与异步方法的return 1相呼应
            {
                Console.WriteLine("Entering into asyn function");
                await Task.Run(WriteN);
                Console.WriteLine("Negative over...");
                await Task.Run(WriteP);
                Console.WriteLine("Positve over...`");
                return 1;
            }
            public static void WriteN()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("-");
                }
            }
            public static void WriteP()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("+");
                }
            }
            
        }
       
        class Program
        {
            public static void Main(string[] args)
            {
                Task<int> task=DoStuff.FindSeriesSum();
                Console.WriteLine("wait asyn function...");
                var t = task.Result;
                Console.WriteLine("all over...");
                Console.WriteLine(t);
            }
    
        }
    }
    

    image-20211110224533518

    可以发现,当遇到第一个await,程序直接返回调用方法的控制流(打印出wait async function,此后便在task.Wait处等待异步方法的结束),同时开始执行await后的任务,执行完第一个await任务,在异步控制流中继续前进,打印完Negative over...后,又碰到第二个await任务,返回到调用方法的控制流,此时,实际上调用方法的控制流处于等待状态,在task.Result处,同时开始执行第二个任务,直到执行完毕后退出(打印Positive over...),调用方法的控制流继续前进,打印all over...,整个程序结束。

    当去掉task.Result后,发现调用程序结束,异步方法也被迫结束。

     public static void Main(string[] args)
            {
                Task<int> task=DoStuff.FindSeriesSum();
                Console.WriteLine("wait asyn function...");
                Console.WriteLine("all over...");
            }
    

    image-20211110231742792

    (2)返回类型是Task的异步方法

    namespace ConsoleApp
    {
        static class DoStuff
        {
            public  static async  Task FindSeriesSum()//返回值是Task,这意味着在调用方法的控制流中必须使用task.Wait()方法来使调用方法的控制流在此处等异步方法执行完毕,再继续前进。
            {
                Console.WriteLine("Entering into asyn function");
                await Task.Run(WriteN);
                Console.WriteLine("Negative over...");
                await Task.Run(WriteP);
                Console.WriteLine("Positve over...`");
                return;//可有可无,若有的话,像这样即使异步方法出现了return,也不会返回任何东西,它只是退出了。
            }
            public static void WriteN()
            {
                for(int i = 0; i < 2000; i++)
                {
                    Console.Write("-");
                }
            }
            public static void WriteP()
            {
                for(int i = 0; i < 2000; i++)
                {
                    Console.Write("+");
                }
            }
            
        }
       
        class Program
        {
            public static void Main(string[] args)
            {
                var sometask=DoStuff.FindSeriesSum();
                Console.WriteLine("wait asyn function...");
                sometask.Wait();
                Console.WriteLine("all over...");
            }
            private static void CountBig(int p)
            {
                for (int i = 0; i < p; i++) ;
            }
        }
    }
    

    image-20211110223147735

    可以发现,当遇到第一个await,程序直接返回调用方法的控制流(打印出wait async function,此后便在task.Wait处等待异步方法的结束),同时开始执行await后的任务,执行完第一个await任务,在异步控制流中继续前进,打印完Negative over...后,又碰到第二个await任务,返回到调用方法的控制流,此时,实际上调用方法的控制流处于等待状态,在someTask.Wait()处,同时开始执行第二个任务,直到执行完毕后退出(打印Positive over...),调用方法的控制流继续前进,打印all over...,整个程序结束。

    同样地,当去掉task.Wait,会发现随着调用方法的控制流结束,异步方法的控制流也被迫结束。

    (3)返回类型是void的异步方法,即fire and forget模式

    namespace ConsoleApp
    {
        static class DoStuff
        {
            public  static async  void FindSeriesSum() //返回类型是void,所以是fire and forget模式,这意味着,调用方法的控制流结束后,异步方法的控制流也必须停止。
            {
                Console.WriteLine("Entering into asyn function");
                await Task.Run(WriteN);
                Console.WriteLine("Negative over...");
                await Task.Run(WriteP);
                Console.WriteLine("Positve over...`");
                return;//可有可无,若有,像这样,它并不返回任何东西,只是退出了。
            }
            public static void WriteN()
            {
                for(int i = 0; i < 2000; i++)
                {
                    Console.Write("-");
                }
            }
            public static void WriteP()
            {
                for(int i = 0; i < 2000; i++)
                {
                    Console.Write("+");
                }
            }
            
        }
       
        class Program
        {
            public static void Main(string[] args)
            {
                DoStuff.FindSeriesSum();
                Console.WriteLine("wait asyn function...");
                Console.WriteLine("all over...");
            }
    
        }
    }
    
    

    image-20211110223903676

    可以看到,异步方法中的控制流在调用方法的控制流结束时,被打断。

    class Program
       {
            public static async Task DoRun()
            {
                Console.WriteLine("Enter DoRun...");
                await Task.Run(WriteP);
                Console.WriteLine("Now WriteN...");
                await Task.Run(WriteN);
            }
            public static void WriteP()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("+");
                }
            }
            public static void WriteN()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("-");
                }
            }
           
            static void Main()
            {
                Console.WriteLine("Test begin...");
                Task task = DoRun();
                Console.WriteLine("Sleep..");
                Thread.Sleep(5);
                Console.WriteLine("After sleep...");
                task.Wait();
                Console.WriteLine("Over...");
            }
        }
    

    output:

    Test begin...
    Enter DoRun...
    +++++Sleep..
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Now WriteN...
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------After sleep...
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Over...
    
    

    await表达式

    await表达式指定了一个异步执行的任务。语法非常简单,就是await关键字,后面跟上一个awaitable类型的实例,万幸的是,我们并不需要构建自己的awaitable,我们可以使用Task类,Task类就是awaitable类型,这也能满足我们大多数需求。在.NET4.5后,还可以用Task<T>类型对象,它也是awaitable类型,用以和await关键字搭配,组成用以异步执行的任务。

    而创建一个Task类,最简单的就是使用Task.Run方法来创建一个Task,其中Task.Run的重载的返回类型和签名如下表:

    返回类型 签名
    Task Run(Action action)
    Task Run(Action action,CancellationToken token)
    Task Run(Func function)
    Task Run(Func function,CancellationToken token)
    Task Run(Func function)
    Task Run(Func function)
    Task Run(Func<Task> function)
    Task Run(Func<Task> function, CancellationTokeen token)
            public static void Main(string[] args)
            {
                Console.WriteLine("Test begin...");
                Task task = DoWorkAsyn();
                Console.WriteLine("wait async function");
                task.Wait();
                Console.WriteLine("Press any key to exit");
                Console.Read();
            }
            public static async Task DoWorkAsyn()
            {
                await Task.Run(()=>Console.WriteLine(5.ToString()));
                Console.WriteLine((await Task.Run(()=>6)).ToString());
                await Task.Run(() => Task.Run(() => Console.WriteLine(7.ToString())));
                int value = await Task.Run(() => Task.Run(() => 8));
                Console.WriteLine(value);
            }
    

    output:

    Test begin...
    wait async function
    5
    6
    7
    8
    Press any key to exit
    
    • 取消一个异步操作

    System.Threading.Tasks命名控件有两个类是为此目的而设计的:CancellationTokenCancellationTokenSource

       class Program
        {
            public static void Main(string[] args)
            {
                CancellationTokenSource cts = new CancellationTokenSource();
                CancellationToken token = cts.Token;
                MyClass mc = new MyClass();
                
                Task t = mc.RunAsync(token);
                //Thread.Sleep(3000);
                //cts.Cancel();
    
                t.Wait();
                Console.WriteLine("Was Cancelled:{0}", token.IsCancellationRequested);
            }
        }
        class MyClass
        {
            public async Task RunAsync(CancellationToken ct)
            {
                Console.WriteLine("Enter in RunAsync");
                if (ct.IsCancellationRequested) return;
                await Task.Run(() => CycleMethod(ct), ct);
            }
            void CycleMethod(CancellationToken ct)
            {
                Console.WriteLine("Starting CycleMethod");
                const int max = 5;
                for(int i = 0; i < max; i++)
                {
                    if (ct.IsCancellationRequested)
                        return;
                    Thread.Sleep(1000);
                    Console.WriteLine("{0} of {1} iterations completed", i + 1, max);
                }
            }
        }
    

    output:

    Enter in RunAsync
    Starting CycleMethod
    1 of 5 iterations completed
    2 of 5 iterations completed
    3 of 5 iterations completed
    4 of 5 iterations completed
    5 of 5 iterations completed
    Was Cancelled:False
    

    取消注释后:

    output:

    Enter in RunAsync
    Starting CycleMethod
    1 of 5 iterations completed
    2 of 5 iterations completed
    3 of 5 iterations completed
    Was Cancelled:True
    
    • 在调用方法中同步的等待任务
    class Program
       {
            public static async Task DoRun(Action act)
            {
                Console.WriteLine("Enter DoRun...");
                await Task.Run(act);
            }
            public static void WriteP()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("+");
                }
            }
            public static void WriteN()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("-");
                }
            }
           
            static void Main()
            {
                Task task1 = DoRun(WriteP);
                Task task2 = DoRun(WriteN);
                Task.WaitAll(task1, task2);
                Console.WriteLine("Task 1:{0} Finished", task1.IsCompleted ? "" : "Not");
                Console.WriteLine("Task 2:{0} Finished", task2.IsCompleted ? "" : "Not");
            }
        }
    

    output:

    Enter DoRun...
    ++++++++++++++Enter DoRun...
    +++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Task 1: Finished
    Task 2: Finished
    

    将上面的Task.WaitAll改为Task.WaitAny,output:

    Enter DoRun...
    Enter DoRun...
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------Task 1: Finished
    Task 2:Not Finished
    ------------------------------------------------
    
    • 在异步方法中异步地等待任务
    class Program
       {
            public static async Task DoRun()
            {
                Console.WriteLine("Enter DoRun...");
                Task task1 = Task.Run(WriteP);
                Task task2 = Task.Run(WriteN);
                await Task.WhenAll(new List<Task> { task1, task2 });
                Console.WriteLine("Task 1:{0} Finished", task1.IsCompleted ? "" : "Not");
                Console.WriteLine("Task 2:{0} Finished", task2.IsCompleted ? "" : "Not");
            }
            public static void WriteP()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("+");
                }
            }
            public static void WriteN()
            {
                for(int i = 0; i < 1000; i++)
                {
                    Console.Write("-");
                }
            }
           
            static void Main()
            {
                Console.WriteLine("Test begin...");
                Task tasks = DoRun();
                tasks.Wait();
                Console.WriteLine("Over...");
                
            }
           
        }
    

    output:

    Test begin...
    Enter DoRun...
    ++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Task 1: Finished
    Task 2: Finished
    Over...
    

    在异步方法中,异步的等待,直到两个任务都完成了,才在异步方法中继续进行下一步,因此打印完毕+-后,最后在异步中给出两个任务的状态。

            static void Main()
            {
                Task t = BadAsyn();
                t.Wait();
                Console.WriteLine("Task status:{0}", t.Status);
                Console.WriteLine("Task IsFaulted:{0}", t.IsFaulted);
                
            }
            public static async Task BadAsyn()
            {
                try
                {
                    await Task.Run(() => throw new Exception());
                }
                catch
                {
                    Console.WriteLine("Exception in BadAsync");
                }
            }
    

    output:

    Exception in BadAsync
    Task status:RanToCompletion
    Task IsFaulted:False
    
    • Task.Delay()
    static void Main()
            {
                Console.WriteLine("Test begins...");
                TestAsyn();
                Console.WriteLine("Test ends...");
                Console.ReadLine();//阻塞调用方法所在的主线程控制流
            }
            public static async void TestAsyn()
            {
                Console.WriteLine("Enter testAsyn");
                await Task.Delay(500);//异步方法控制流中的“暂停”,不影响调用方法主线程控制流
                Console.WriteLine("After Delay...");
            }
    

    output:

    Test begins...
    Enter testAsyn
    Test ends...
    After Delay...
    

    在GUI中执行异步操作

    实际上,异步方法在GUI程序上非常有用,因为GUI程序在设计上要求所有的显示变化都必须在主GUI线程中完成,如点击按钮,展示标签,移动窗体等(各种消息),Windows是通过消息来实现这一点的,消息被放入由消息泵管理的消息队列中。

    消息泵从队列中取出一条消息,并调用它的处理程序代码,当处理程序代码完成后,消息泵获取下一个消息,并循环此过程。 也正因为此,处理程序就必须执行时间要很短,这样才不至于挂起阻碍其他消息的处理,否则消息队列中的消息产生积压,程序失去相应响应。

    <StackPanel>
            <Label Name="lblStatus" Margin="10,5,10,0">Not Doing Anything</Label>
            <Button x:Name="btnDoStuff" Content="Do Stuff" HorizontalAlignment="Left" Margin="10,5" Padding="5,2" Click="btnDoStuff_Click"/>
        </StackPanel>
    
      public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
            {
                btnDoStuff.IsEnabled = false;
                lblStatus.Content = "Doing Stuff";
                //Thread.Sleep(4000);
                await Task.Delay(4000);
                lblStatus.Content = "Not Doing Anything";
                btnDoStuff.IsEnabled = true;
            }
        }
    

    image-20211111232517053

    image-20211111232544772

    使用async/await可以将该点击消息的前两条消息压入队列,然后将自己从处理器“摘下”,4秒后将自己剩余部分压入队列。

    BackgroundWorker

    async/awaiter特性更适合那些需要在后台完成的不想关的小任务,但有时候,可能需要另建一个线程,在后台持续运行以完成某项工作,并不时地与主线程进行通信,而这正是BackgroundWorker所要做的。

    BackgroundWorker的属性有WokerReportsProgressWokerSupportsCancellationIsBusy,CancellationPending,方法有RunWorkerAsync()CancelAsync()ReportsProgress(),事件有DoWorkProgressChangedRunWorkerCompleted

    其中:

    WorkerReportsProgress用于设置BackgroundWorker是否将它的进度汇报给主线程,WorkerSupportsCancellation用于设置是否允许主线程取消BackgroundWorker的工作。

    RunWorkerAsync方法负责在主线程中启动BackgroundWorker,同时触发DoWork事件,而在DoWork事件中,可以添加需要完成的任务的代码,如果要给主线程汇报进度,可调用ReportsProgress()方法,而ReportsProgress()方法又触发ProgressChanged事件,后台程序正在工作,则其IsBusy属性就是true。

    如果允许主线程取消后台线程任务,主线程调用CancelAsync方法,该方法会使BackgroundWorkerIsCancellationPending属性为true。

    事件处理程序的委托如下:

    void DoWorkEventHandler(object sender,DoWorkEventArgs e);
    void ProgressChangedEventHandler(object sender,ProgressChangedEventArgs e);
    void RunWorkerCompletedEventHandler(object sender,RunWorkerCompletedEventArgs e);
    

    这些事件处理程序的sender自然都是发起事件的BackgroundWorker,关于args的说明参考微软文档:

    • DoworkEventArgs
    image-20211113100246831
    • ProgressChangedEventArgs

    ProgressPercentage是传给主线程的后台线程任务完成的进度值,也是ReportsProcess的参数。

    image-20211113100638603

    RunWorkerCompletedEventArgs

    image-20211113100900176 image-20211113101036282

    例子:

        <StackPanel>
            <ProgressBar x:Name="progressBar" Height="20" Width="200" Margin="10"/>
            <Button x:Name="btnProcess" Width="100" Click="btnDoStuff_Click">Process</Button>
            <Button x:Name="btnCancel" Width="100" Click="btnCancel_Click">Cancel</Button>
        </StackPanel>
    
     public partial class MainWindow : Window
        {
            BackgroundWorker bgworker = new BackgroundWorker();
            public MainWindow()
            {
                InitializeComponent();
                bgworker.WorkerReportsProgress = true;//允许向主线程汇报进度
                bgworker.WorkerSupportsCancellation = true;//允许主线程取消后台任务
                bgworker.DoWork += DoWork_Handler;//注册DoWork事件
                bgworker.ProgressChanged += ProgressChanged_Handler;//注册ProgressChanged事件
                bgworker.RunWorkerCompleted += Bgworker_RunWorkerCompleted;//注册RunWorkCompleted事件
            }
    
            private void Bgworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                //完成后的操作,不管是正常结束还是被取消的
                progressBar.Value = 0;
                if (e.Cancelled)
                    MessageBox.Show("被取消", "有内鬼,任务取消");
                else
                    MessageBox.Show("完成", "任务完成");
            }
    
            private void btnDoStuff_Click(object sender, RoutedEventArgs e)
            {
                if (!bgworker.IsBusy)
                {
                    bgworker.RunWorkerAsync(); //触发Dowork事件
                }
            }
            private void DoWork_Handler(object sender,DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;
                for(int i = 1; i <= 10; i++)
                {
                    if (worker.CancellationPending) //不断检查Cancellation属性,如果true,则将e的Cancel属性
                    {                              //设置为true,这样RunCompletedEventArgs的Cancelled属性就也被设定为true
                        e.Cancel = true;
                        break;
                    }
                    else
                    {
                        worker.ReportProgress(i * 10);//否则,向主线程汇报进度,触发ProgressChanged事件,参数传给该事件的
                                                      //ProgressChangedEventArgs的ProgressPercentage属性
                        Thread.Sleep(500);//模拟time-consuming 任务
                    }
                }
            }
            private void ProgressChanged_Handler(object sender, ProgressChangedEventArgs e)
            {
                progressBar.Value = e.ProgressPercentage;//将e的ProgressPerceentage值传入进度条
            }
            
            private void btnCancel_Click(object sender, RoutedEventArgs e)
            {
                bgworker.CancelAsync();
            }
        }
    
    image-20211113102805820 image-20211113102836094 image-20211113102909225
    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    httpclient 使用问题记录:org.apache.http.HttpException: Unsupported Content-Coding:GLZip
    Gitserver端密码变更,但是本地gitconfig配置未变更账号和密码问题解决
    线程池ThreadPoolExecutor学习
    Java 网络编程
    org.apache.ibatis.binding.BindingException: Invalid bound statement Mybatis绑定错误问题解决
    Java string类
    maven3.6.2 版本 在idea 2019.2.2下遇到的问题解决记录
    python
    django-URL与视图配置
    python 的datetime模块使用
  • 原文地址:https://www.cnblogs.com/johnyang/p/15536498.html
Copyright © 2011-2022 走看看