您的位置:首页 > 其它

4.0中的并行计算和多线程详解(二)

2012-05-06 14:16 369 查看

  多线程部分

  多线程在4.0中被简化了很多,仅仅只需要用到System.Threading.Tasks.::.Task类,下面就来详细介绍下Task类的使用。

  一、简单使用

  开启一个线程,执行循环方法,返回结果。开始线程为Start(),等待线程结束为Wait()。

Code

/// <summary>

/// Task简单使用

/// </summary>

private void Demo1()

{

int i = 0;

Random r = new Random(DateTime.Now.Second);

Task t = new Task(() =>

{

for (int v = 0; v < 100; v++)

i += r.Next(100);

});

t.Start();

t.Wait();

Console.WriteLine("这是执行Task1后等待完成:" + i.ToString());

Console.ReadLine();

}

  比以前使用Thread方便多了吧。上面的例子是使用外部的变量获得结果,下面的例子是用Task<T>直接返回结果,当调用Result属性时,会自动等待线程结束,等同调用了Wait()。代码如下:

Code

/// <summary>

/// Task带返回值

/// </summary>

private void Demo2()

{

Random r = new Random(DateTime.Now.Second);

Task<int> t = new Task<int>(() =>

{

int i = 0;

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

});

t.Start();

Console.WriteLine("这是执行Task1获取返回值:" + t.Result.ToString());

Console.ReadLine();

}

  总结1:Task的使用比Thread简单很多,减少了同步,等待等等问题,唯一的遗憾是不支持Thread的IsBackground。

  结论1:如果不需要使用IsBackground,那么尽情的使用Task吧。

  二、线程执行完毕后调用另一个线程

  也就是两个线程,有序的执行,这里使用ContinueWith(),t执行完毕后再执行一个task方法,不多说了代码如下:

Code

/// <summary>

/// Task 执行完毕后调用另一个Task

/// </summary>

private void Demo3()

{

Random r = new Random(DateTime.Now.Second);

Task<int> t = new Task<int>(() =>

{

int i = 0;

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

});

t.ContinueWith((Task<int> task) =>

{

Console.WriteLine("这是执行完毕Task1后继续调用Task2:" + task.Result.ToString());

});

t.Start();

Console.ReadLine();

}

  也可以直接链式的写下去,代码如下:

Code

/// <summary>

/// Task 执行完毕后调用另一个Task(链式写法)

/// </summary>

private void Demo4()

{

Random r = new Random(DateTime.Now.Second);

Task<int> t = new Task<int>(() =>

{

int i = 0;

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

});

Task t2 = t.ContinueWith((Task<int> task) =>

{

Console.WriteLine(task.Result.ToString());

});

t2.ContinueWith(task =>

{

Console.WriteLine("这是执行完毕Task1后继续调用Task2,Task2后调用Task3。");

});

t.Start();

Console.ReadLine();

}

  结论2:Task可以便捷的将几个方法串行执行。

  三、带有父子关系的线程/多线程并行开启

  t带有t1,t2,t3三个子线程,执行t的时候t1,t2,t3可并行处理,t必须等待t1,t2,t3都执行完毕后才能结束。创建子Task时候必须指定参数为AttachedToParent。

Code

/// <summary>

/// 带有父子关系的Task集合,[TaskCreationOptions.AttachedToParent]

///

/// 值 说明

/// None 默认值,此Task会被排入Local Queue中等待执行,采用LIFO模式。

/// AttachedToParent 建立的Task必须是外围的Task的子Task,也是放入Local Queue,採LIFO模式。

/// LongRunning 建立的Task不受Thread Pool所管理,直接新增一个Thread来执行此Task,无等待、无排程。

/// PreferFairness 建立的Task直接放入Global Queue中,採FIFO模式。(比上面的优先级低)

/// </summary>

private void Demo5()

{

Task<int> t = new Task<int>(() =>

{

Task<int> t1 = new Task<int>(() =>

{

int i = 0;

Random r = new Random(DateTime.Now.Second);

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

}, TaskCreationOptions.AttachedToParent);

Task<int> t2 = new Task<int>(() =>

{

int i = 0;

Random r = new Random(DateTime.Now.Second);

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

}, TaskCreationOptions.AttachedToParent);

Task<int> t3 = new Task<int>(() =>

{

int i = 0;

Random r = new Random(DateTime.Now.Second);

for (int v = 0; v < 100; v++)

i += r.Next(100);

return i;

}, TaskCreationOptions.AttachedToParent);

t1.Start();

t2.Start();

t3.Start();

return t1.Result + t2.Result + t3.Result;

});

t.Start();

t.Wait();

Console.WriteLine("这是带有父子关系的Task集合:" + t.Result.ToString());

Console.ReadLine();

}

  结论3:多个线程的同时开启在这里也很方便,也不用担心同步等问题。

  四、Task的中断

  这个很复杂,就不多说了,代码中有比较详细的介绍。

Code

/// <summary>

/// 中途取消Task执行,Token

///

/// 一是正常结束、二是产生例外、三是透过Cancel机制,这三种情况都会反映在Task.Status属性上

/// 值 说明

/// Created Task已经建立,但未呼叫Start。

/// WaitingForActivation Task已排入排程,但尚未执行(一般我们建立的Task不会有此状态,只有ContinueWith所产生的Task才会有此状态)。

/// WaitingToRun Task已排入排程,等待执行中。

/// Running Task执行中。

/// WaitingForChildrenToComplete Task正等待子Task結束。

/// RanToCompletion Task已经正常执行完毕。

/// Canceled Task已被取消。

/// Faulted Task执行中发生未预期例外。

///

/// 除了Status属性外,Task还提供了另外三个属性来判定Task状态。

/// 属性 说明

/// IsCompleted Task已经正常执行完毕。

/// IsFaulted Task执行中法生未预期例外。

/// IsCanceled Task已被取消。

/// </summary>

private void Demo6()

{

CancellationTokenSource cts = new CancellationTokenSource();

var ctoken = cts.Token;

Task t1 = new Task(() =>

{

for (int v = 0; v < 10; v++)

{

if (ctoken.IsCancellationRequested)

{

//第一种写法

//这个会抛出异常

ctoken.ThrowIfCancellationRequested();

//另一种写法

//这个不会返回异常,但是获取不到是否是中断还是执行完毕。

//return;

}

Thread.Sleep(1000);

Console.WriteLine(v);

}

}, ctoken);

t1.Start();

Thread.Sleep(2000);

cts.Cancel();

try

{

t1.Wait();

}

catch

{

if (t1.IsCanceled)

Console.WriteLine("cancel");

}

Console.ReadLine();

cts.Dispose();

}

  结论4:中断很复杂,但是对一般逻辑来说,是没有很大必要的。

  五、其他

  这里介绍下另一种写法Task.Factory,以及ContinueWhenAny和ContinueWhenAll两个方法。Task.Factory是静态工厂类,用于对Task提供一些麻烦的支持,这里主要介绍ContinueWhenAny和ContinueWhenAll。

ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。好了,还是看代码:

Code

/// <summary>

/// Task.Factory

/// ContinueWhenAny和ContinueWhenAll

/// ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。

/// ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。

/// </summary>

private void Demo7()

{

Task<int> t1 = Task.Factory.StartNew<int>(() =>

{

int total = 0;

for (int i = 0; i < 10; i++)

total += i;

Thread.Sleep(12000);

return total;

});

Task<int> t2 = Task.Factory.StartNew<int>(() =>

{

int total = 0;

for (int i = 10; i < 20; i++)

total += i;

Thread.Sleep(10000);

return total;

});

Task tfinal = Task.Factory.ContinueWhenAny<int>(

new Task<int>[] { t1, t2 }, (Task<int> task) =>

{

if (task.Status == TaskStatus.RanToCompletion)

{

Console.WriteLine(task.Result);

}

});

Console.ReadLine();

}

  结论5:ContinueWhenAny和ContinueWhenAll对特定条件执行,还是有些用处的。

  好了,这篇文章算是完结了,4.0中的并行和线程确实简单了很多,使用起来也很方便,为了性能的提升还是要适当的使用下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: