Task是一个创建新线程去完成调用的类,但是使用中有以下几个问题, 1.task可否带参数操作?能否返回参数? 2.主线程和task新线程有哪些约束?
下面,只做简洁的操作示范
// 测试按钮的操作流程 private void button_Click(object sender, RoutedEventArgs e) { //方式零 Task<string> BarCodeTask0 = new Task<string>(GetBarCode0, "0");//有参数有返回值 BarCodeTask0.Start(); //方式一 Task<string> BarCodeTask1 = Task.Run<string>(() => GetBarCode1("1")); //方式二 Task<string> BarCodeTask2 = Task<string>.Factory.StartNew(() => GetBarCode1("2")); Task.WaitAll(new Task[] { BarCodeTask1,BarCodeTask2}, 500); //UI线程调用此方法会导致UI线程阻塞到此新线程结束 string Result0 = BarCodeTask0.Result; string Result1 = BarCodeTask1.Result; //UI线程调用此属性会导致UI线程阻塞到此新线程结束 string Result2 = BarCodeTask2.Result; tb_COM2.Text = Result0; tb_Jig1.Text = Result1; tb_Jig2.Text = Result2; } private string GetBarCode1(string i) { if (i=="1") { Thread.Sleep(2000); this.Dispatcher.BeginInvoke(new Action(() => { tb_Code1.Text = "wo111"; })); return "A"; } else if (i == "2") { Thread.Sleep(3000); this.Dispatcher.BeginInvoke(new Action(() => { tb_Code2.Text = "wo222"; })); return "B"; } else { return "C"; } } private string GetBarCode0(object i) { if (i.ToString() == "0") { Thread.Sleep(1500); this.Dispatcher.BeginInvoke(new Action(() => { tb_COM1.Text = "我是000"; })); return "A0"; } else { return "C0"; } }现在我们来回答上面两个问题,
1.可以带参数,也可以有返回值,并且接受返回值。 1.1 第一种方式采用NEW关键字,调用时只需要函数名即可,但是,此函数的传入参数类型必须为object,其中<string>标志为返回值类型; 如果不需要传参,调用函数不传参即可; 如果不需要返回值,去掉<string>即可; 1.2 第二种,第三种,如出一辙,只是Factory方法的创建可以指定新开的线程为常开式,不被回收,因为正常情况下,我们新开的线程执行完操作后,系统都会自动回收线程到线程池中备用; 这里需要强调的是,调用方式() => GetBarCode1("1")和第一种的不同,此处用到委托的方式,被调函数可以正常书写,不用考虑必须传入object类型,对于我而言,这种调用更符合我一直的认知习惯;
2.正如代码片段中注释的一样,他们分为不同上下文的关系 2.1 Task.WaitAll方法是等待新开的task执行完,而调用却是在UI线程里(主线程),如果新task线程没有完成,UI线程也将被阻塞,这个还是比较好理解的;其中500为超时操作,也就是过了500ms 等待的task还没有完成将不再阻塞UI线程,向下执行; 2.2 Task.Result是获取对应task执行操作完后返回的值,需要强调的是,此操作在UI线程里调用,同样会阻塞UI线程,直到对应task执行结束后,才会解除阻塞,这点,需要重点注意;
=还有一点要强调=,死锁 这个方法是在task新线程中操作UI控件, //BeginInvoke异步,Invoke同步,在新线程中调用时,Invoke同步会导致新线程结束后返回UI线程,造成死锁;而BeginInvoke异步操作完后,直接结束此新线程,不会返回UI主线程;
By:世上思维千千万,自己感觉爽歪歪!