可爱午餐对对碰,哈飞百利怎么样,oppoa201手机杀毒软件
知识需要不断积累、总结和沉淀,思考和写作是成长的催化剂
一、任务task二、并行parallel1、parallel.for()、parallel.foreach()2、parallel.for3、parallel.invoke()4、plinq三、异步等待asyncawait
system.threading.tasks在.net4引入,前面线程的api太多了,控制不方便,而threadpool控制能力又太弱,比如做线程的延续、阻塞、取消、超时等功能不太方便,所以task就抽象了线程功能,在后台使用threadpool
可以使用taskfactory类或task类的构造函数和start()方法,委托可以提供带有一个object类型的输入参数,所以可以给任务传递任意数据,还漏了一个常用的task.run
taskfactory taskfactory = new taskfactory();
taskfactory.startnew(() =>
{
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task.factory.startnew(() =>
{
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task task = new task(() =>
{
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task.start();
只有task类实例方式需要start()
去启动任务,当然可以runsynchronously()
来同步执行任务,主线程会等待,就是用主线程来执行这个task任务
task task = new task(() =>
{
thread.sleep(10000);
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task.runsynchronously();
在thread中我们使用join来阻塞等待,在多个thread时进行控制就不太方便。task中我们使用实例方法wait
阻塞单个任务或静态方法waitall和waitany
阻塞多个任务
var task = new task(() =>
{
thread.sleep(5*1000);
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
var task2 = new task(() =>
{
thread.sleep(10 * 1000);
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task.start();
task2.start();
//task.wait();//单任务等待
//task.waitany(task, task2);//任何一个任务完成就继续
task.waitall(task, task2);//任务都完成才继续
如果不希望阻塞主线程,实现当一个任务或几个任务完成后执行别的任务,可以使用task静态方法whenall和whenany
,他们将返回一个task,但这个task不允许你控制,将会在满足whenall和whenany里任务完成时自动完成,然后调用task的continuewith方法,就可以在一个任务完成后紧跟开始另一个任务
task.whenall(task, task2).continuewith((t) =>
{
console.writeline($"tid={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task.factory工厂中也存在类似continuewhenall和continuewhenany
不仅可以在一个任务结束后执行另一个任务,也可以在一个任务内启动一个任务
,这就启动了一个父子层次结构
var parenttask = new task(()=>
{
console.writeline($"parentid={thread.currentthread.managedthreadid},datetime={datetime.now}");
thread.sleep(5*1000);
var childtask = new task(() =>
{
thread.sleep(10 * 1000);
console.writeline($"childid={thread.currentthread.managedthreadid},datetime={datetime.now}")
});
childtask.start();
});
parenttask.start();
如果父任务在子任务之前结束,父任务的状态为waitingforchildrentocomplete,当子任务也完成时,父任务的状态就变为rantocompletion,当然,在创建任务时指定taskcreationoptions枚举参数,可以控制任务的创建和执行的可选行为
简单介绍下创建任务中的taskcreationoptions
枚举参数,创建任务时我们可以提供taskcreationoptions枚举参数,用于控制任务的创建和执行的可选行为的标志
另一个枚举参数是continuewith方法中的taskcontinuationoptions
枚举参数,它除了拥有几个和上面同样功能的枚举值外,还拥有控制任务的取消延续等功能
cancellationtokensource source = new cancellationtokensource();
source.cancel();
var task1 = new task(() =>
{
console.writeline($"task1 id={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
var task2 = task1.continuewith(t =>
{
console.writeline($"task2 id={thread.currentthread.managedthreadid},datetime={datetime.now}");
},source.token);
var task3 = task2.continuewith(t =>
{
console.writeline($"task3 id={thread.currentthread.managedthreadid},datetime={datetime.now}");
});
task1.start();
上面例子我们企图task1->task2->task3
顺序执行,然后通过cancellationtoken来取消task2的执行。结果会是怎样呢?结果task1和task3会并行执行(task3也是会执行的,而且是和task1并行,等于原来的一条链变成了两条链),然后我们尝试使用lazycancellation,
var task2 = task1.continuewith(t =>
{
console.writeline($"task2 id={thread.currentthread.managedthreadid},datetime={datetime.now}");
},source.token,taskcontinuationoptions.lazycancellation,taskscheduler.current);
这样,将会在task1执行完成后,task2才去判断source.token,为cancel就不执行,接下来执行task3就保证了原来的顺序
在上篇使用thread时,我们使用一个变量isstop标记是否取消任务,这种访问共享变量的方式难免会出问题。task中提出cancellationtokensource类专门处理任务取消,常见用法看下面代码注释
cancellationtokensource source = new cancellationtokensource();//构造函数中也可指定延迟取消
//注册一个取消时调用的委托
source.token.register(() =>
{
console.writeline("当前source已经取消,可以在这里做一些其他事情(比如资源清理)...");
});
var task1 = new task(() =>
{
while (!source.iscancellationrequested)
{
console.writeline($"task1 id={thread.currentthread.managedthreadid},datetime={datetime.now}");
}
},source.token);
task1.start();
//source.cancel();//取消
source.cancelafter(1000);//延时取消
让子线程返回结果,可以将信息写入到线程安全的共享变量
中去,或则使用可以返回结果的任务。使用task的泛型版本task<tresult>
,就可以定义返回结果的任务。task是继承自task的,result获取结果时是要阻塞等待直到任务完成返回结果的,内部判断没有完成则wait。通过taskstatus属性可获得此任务的状态是启动、运行、异常还是取消等
var task = new task<string>(() =>
{
return "hello ketty";
});
task.start();
string result = task.result;
可以使用aggregateexception
来接受任务中的异常信息,这是一个聚合异常继承自exception,可以遍历获取包含的所有异常,以及进行异常处理,决定是否继续往上抛异常等
var task = task.factory.startnew(() =>
{
var childtask1 = task.factory.startnew(() =>
{
throw new exception("childtask1异常...");
},taskcreationoptions.attachedtoparent);
var childtask12= task.factory.startnew(() =>
{
throw new exception("childtask2异常...");
}, taskcreationoptions.attachedtoparent);
});
try
{
try
{
task.wait();
}
catch (aggregateexception ex)
{
foreach (var item in ex.innerexceptions)
{
console.writeline($"message{item.innerexception.message}");
}
ex.handle(x =>
{
if (x.innerexception.message == "childtask1异常...")
{
return true;//异常被处理,不继续往上抛了
}
return false;
});
}
}
catch (exception ex)
{
throw;
}
在.net4中,另一个新增的抽象的线程时parallel类。这个类定义了并行的for和foreach的静态方法。parallel.for()和parallel.foreach()方法多次调用一个方法,而parallel.invoke()方法允许同时调用不同的方法
。首先parallel是会阻塞主线程的,它将让主线程也参与到任务中
parallel.for()类似于for允许语句,并行迭代同一个方法,迭代顺序没有保证的
parallelloopresult result = parallel.for(0, 10, i =>
{
console.writeline($"{i} task:{task.currentid} thread:{thread.currentthread.managedthreadid}");
});
console.writeline(result.iscompleted);
也可以提前中断parallel.for()方法。for()方法的一个重载版本接受action<int,parallelloopstate style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">类型参数。一般不使用,像下面这样,本想大于5就停止,但实际也可能有大于5的任务已经在跑了。可以通过paralleloptions传入允许最大线程数以及取消token等
parallelloopresult result = parallel.for(0, 10, new paralleloptions() { maxdegreeofparallelism = 8 },(i,loop) =>
{
console.writeline($"{i} task:{task.currentid} thread:{thread.currentthread.managedthreadid}");
if (i > 5)
{
loop.break();
}
});
for还有一个高级泛型版本,相当于并行的聚合计算
parallelloopresult for<tlocal>(int frominclusive, int toexclusive, func<tlocal> localinit, func<int, parallelloopstate, tlocal, tlocal> body, action<tlocal> localfinally);
像下面这样我们求0…100的和,第三个参数更定一个种子初始值,第四个参数迭代累计,最后聚合
int totalnum = 0;
parallel.for<int>(0, 100, () => { return 0; }, (current, loop, total) =>
{
total += current;
return total;
}, (total) =>
{
interlocked.add(ref totalnum, total);
});
上面for用来处理数组数据,foreach()方法用来处理非数组的数据任务,比如字典数据继承自ienumerable的集合等
parallel.invoke()则可以并行调用不同的方法,参数传递一个action的委托数组
parallel.invoke(() => { console.writeline($"方法1 thread:{thread.currentthread.managedthreadid}"); }
, () => { console.writeline($"方法2 thread:{thread.currentthread.managedthreadid}"); }
, () => { console.writeline($"方法3 thread:{thread.currentthread.managedthreadid}"); });
plinq,为了能够达到最大的灵活度,linq有了并行版本。使用也很简单,只需要将原始集合asparallel就转换为支持并行化的查询。也可以asordered来顺序执行,取消token,强制并行等
var nums = enumerable.range(0, 100);
var query = from n in nums.asparallel()
select new
{
thread=$"tid={thread.currentthread.managedthreadid},datetime={datetime.now}"
};
异步编程模型,可能还需要大篇幅来学习,这里先介绍下基本用法,内在本质需要用ilspy反编译来看,以后可能要分专题总结。文末先给几个参考资料,有兴趣自己阔以先琢磨琢磨鸭
这是.net4.5开始提供的一对语法糖,使得可以较简便的使用异步编程。async用在方法定义前面,await只能写在带有async标记的方法中
,任何方法都可以增加async,一般成对出现,只有async没有意义,只有await会报错,请先看下面的示例
private static async void asynctest()
{
//主线程执行
console.writeline($"before await threadid={thread.currentthread.managedthreadid}");
taskfactory taskfactory = new taskfactory();
task task = taskfactory.startnew(() =>
{
thread.sleep(3000);
console.writeline($"task threadid={thread.currentthread.managedthreadid}");
});
await task;//主线程到这里就返回了,执行主线程任务
//子线程执行,其实是封装成委托,在task之后成为回调(编译器功能 状态机实现) 后面相当于task.continuewith()
//这个回调的线程是不确定的:可能是主线程 可能是子线程 也可能是其他线程,在winform中是主线程
console.writeline($"after await threadid={thread.currentthread.managedthreadid}");
}
一般使用async都会让方法返回一个task的,像下面这样复杂一点的
private static async task<string> asynctest2()
{
console.writeline($"before await threadid={thread.currentthread.managedthreadid}");
taskfactory taskfactory = new taskfactory();
string x = await taskfactory.startnew(() =>
{
thread.sleep(3000);
console.writeline($"task threadid={thread.currentthread.managedthreadid}");
return "task over";
});
console.writeline($"after await threadid={thread.currentthread.managedthreadid}");
return x;
}
通过var reslult = asynctest2().result;调用即可。但注意如果调用wait或result的代码位于ui线程,task的实际执行在其他线程,其需要返回ui线程则会造成死锁,所以应该async all the way
从上面简单示例中可以看出异步编程的执行逻辑:主线程a逻辑->异步任务线程b逻辑->主线程c逻辑
。
异步方法的返回类型只能是void、task、task。示例中异步方法的返回值类型是task,通常void也不推荐使用,没有返回值直接用task就是
上一篇也大概了解到如果我们要在任务中更新ui,需要调用invoke通知ui线程来更新,代码看起来像下面这样,在一个任务后去更新ui
private void button1_click(object sender, eventargs e)
{
var resulttask = task.run(() => {
thread.sleep(5000);
return "任务完成";
});
resulttask.continuewith((r)=>
{
textbox1.invoke(() => {
textbox1.text = r.result;
});
});
}
如果使用async/await会看起来像这样,是不是优雅了许多。以看似同步编程的方式实现异步
private async void button1_click(object sender, eventargs e)
{
var t = task.run(() => {
thread.sleep(5000);
return "任务完成";
});
textbox1.text = await t;
}
https://www.cnblogs.com/opencoder/p/4434574.html
在.net 4.5中引入的async和await两个新的关键字后,用户能以一种简洁直观的方式实现异步编程。甚至都不需要改变代码的逻辑结构,就能将原来的同步函数改造为异步函数。
在内部实现上,async和await这两个关键字由编译器转换为状态机,通过system.threading.tasks中的并行类实现代码的异步执行。
字数有点多了,我的能力也就高考作文800字能写的出奇好。看了很多异步编程,脑袋有点炸,等消化后再输出一次,技艺不足,只能用输出倒逼输入了,下一篇会是线程安全集合、锁问题、同步问题,基于事件的异步模式等
search the fucking web
read the fucking maunal
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
asp.net搭建博客,使用BlogEngine.NET+MySql搭建博客
网友评论