System.Threading.Tasks
前言:
我们之前介绍了两种构建多线程软件的编程技术(使用异步委托或通过System.Threading的成员)。这两个可以在任何版本的.NET平台工作。
从.NET4.0开始,微软引入了一种全新的多线程应用程序开发方法,即使用TPL并行编程库。使用System.Threading.Tasks中的类型,可以构建可扩展的并行代码,而不必直接与线程和线程池打交道。我们使用TPL的时候也可以使用System,Thrading。这两种线程工具可以非常自然地一起工作。
总体而言,System.Threading.Tasks中的类型被称为任务并行库(Task Parallel Library,TPL)。
此命名空间中的一些类。
Parallel类的作用
TPL中一个十分重要的类时 System.Threading.Tasks.Parallel ,它提供了大量方法,能够以并行的方式迭代数据集合(实现了IEnumerable<T>的对象)。在SDK文档中查看Parallel类,你会发现该类支持两个主要的静态方法——Parallel.For()和Parallel.FoeEach(),每个方法都有很大的重载版本。
并行解释:我们知道foreach里面循环一次,然后再循环,再循环,那并行说的是尽可能把这些分开的一次次循环放在一起执行。加快了速度。
方法中的参数有两个可能需要了解一下,等具体在用到的时候在解释。
出了for,foreach方法外:
这些方法可以用来编写并行执行的码体。这些语句的逻辑与普通循环(使用for或foreach关键字)中的逻辑完全相同。好处是,Parallel类将从线程池中为我们提取线程(和管理并发)。这个方法里面还需要使用System.Func<T>和System.Action<T>委托(到时候会专门介绍委托的时候在仔细介绍)来指定要调用的处理数据方法。
Func<T>委托:表示一个拥有给定返回值和不同数量参数的方法。
Action<t>委托:表示指向有几个参数的方法,但返回 void.
我们在使用For(),ForEach()方法时可以传递强类型的Func<T>或Action<T>委托对象,你也可以使用恰当的C#匿名方法或Lambda表达式来简化编程。
使用Parallel类的数据并行
使用TPL的第一种方式是执行数据并行。使用For,ForEach方法以并行方式对数组或集合中的数据进行迭代。
列子:我们把一个文件夹中的图片,进行翻转,然后保存在别的文件夹中
普通的写法:
private void ProcessFiles()
{
string[] files = Directory.GetFiles(@"C:\Users\Seali\Pictures\小牛电动", "*.*", SearchOption.AllDirectories);
string newDir = @"C:\ModefiedPictures";
Directory.CreateDirectory(newDir); foreach (string item in files)
{
string filename = Path.GetFileName(item);
using (Bitmap bitma = new Bitmap(item))
{
bitma.RotateFlip(RotateFlipType.Rotate180FlipNone);//反转180度
bitma.Save(Path.Combine(newDir, filename)); //保存 this.Invoke((Action)delegate
{
textBox1.Text = string.Format("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId);
}); }
}
private void button1_Click(object sender, EventArgs e)
{
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); //用来计算运行时间
ProcessFiles();
double aa=sw.ElapsedMilliseconds / 1000.0;
MessageBox.Show(aa+"s"); //计算我们完成这个方法用了多长时间
}
当我们执行此方法的时候,开始就一直卡这,结束了我们只能看到文本框上显示最后一次的名字,因为我们的线程阻塞了。因为我们在等待这个过程,所有卡顿。
换成我们的Parallel类的方法试一下
string[] files = Directory.GetFiles(@"C:\Users\Seali\Pictures\小牛电动", "*.*", SearchOption.AllDirectories);
string newDir = @"C:\ModefiedPictures";
Directory.CreateDirectory(newDir);
Parallel.ForEach(files, item => //对于集合处理很容易
{
string filename = Path.GetFileName(item);
using (Bitmap bitma = new Bitmap(item))
{
bitma.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitma.Save(Path.Combine(newDir, filename)); this.Invoke((Action)delegate //在form对象上调用,允许次线程以线程安全的方式访问控件
{
textBox1.Text = string.Format("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId);
});
}
});
虽然速度上快了一些,但依然阻塞了。有没有办法让我们的线程不再阻塞,当然是有的,介绍我们的第二个类。
Task
定义:表示一个异步操作。可以作为异步委托的简单替代品。
部分构造函数:
部分属性:
本文由机器翻译。若要查看英语原文,请勾选“英语”复选框。 也可将鼠标指针移到文本上,在弹出窗口中显示英语原文。
|
翻译
英语
|
TaskFactory 类
提供对创建和计划 Task 对象的支持。
部分方法:(此方法有很多的重载)
传入的委托,指向以异步方式进行调用的方法。居然是异步执行了,我们的主线程不再卡顿了,每循环一次文本框都会变一次了。
跟新我们的代码:
//创建一个新的任务来处理文件
Task.Factory.StartNew(() =>
{
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); //用来计算运行时间
ProcessFiles();
double aa = sw.ElapsedMilliseconds / 1000.0;
MessageBox.Show(aa + "s");
});
我们也可以取消请求。
Parallel.For()和Pallel.ForEach()方法都支持取消标记,我们调用方法时,传入一个ParallelOption对象,它包含一个CancekkationTokenSource对象。
ParallelOptions 类
存储配置的方法的操作的选项 Parallel 类。
属性:
CancellationToken 结构
传播有关应取消操作的通知。
除此之外我们还需要了解
CancellationTokenSource 类
向应该被取消的 CancellationToken 发送信号。
所有,我们新加一个取消按钮。完整代码如下
private CancellationTokenSource cancelToken = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
//创建一个新的任务来处理文件
Task.Factory.StartNew(() =>
{
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); //用来计算运行时间
ProcessFiles();
double aa = sw.ElapsedMilliseconds / 1000.0;
MessageBox.Show(aa + "s");
});
} private void ProcessFiles()
{
//设置参数
ParallelOptions parOpts = new ParallelOptions();
parOpts.CancellationToken = cancelToken.Token;
parOpts.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
string[] files = Directory.GetFiles(@"C:\Users\Seali\Pictures\小牛电动", "*.*", SearchOption.AllDirectories);
string newDir = @"C:\ModefiedPictures";
Directory.CreateDirectory(newDir);
try
{
Parallel.ForEach(files, item =>
{
parOpts.CancellationToken.ThrowIfCancellationRequested();//抛异常
string filename = Path.GetFileName(item);
using (Bitmap bitma = new Bitmap(item))
{
bitma.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitma.Save(Path.Combine(newDir, filename)); this.Invoke((Action)delegate //在form对象上调用,允许次线程以线程安全的方式访问控件
{
textBox1.Text = string.Format("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId);
}); }
});
}
catch (OperationCanceledException ex)
{
this.Invoke((Action)delegate //在form对象上调用,允许次线程以线程安全的方式访问控件
{
textBox1.Text = ex.Message;
});
}
} private void button2_Click(object sender, EventArgs e)
{
//停止所有的工作者线程
cancelToken.Cancel();
}
以上介绍的是数据并行处理。
使用注意:如果进行数据并行处理,循环的时候是字符串的追加,很小的机会会报错,这个需要注意下
例如: 我把循环换成了这个,发现有时候页面上显示有问题,上一次还没写完,下一次就已经开始了,然后拼接字符串就出现了问题,如果前一次跟下一次做的操作没什么联系用这个就很好
//Parallel.ForEach(dt.AsEnumerable().AsParallel(),
//item =>
//{
// sb.Append("<section class=\"list\" ><span class=\"wenbe hundred\">");
// sb.AppendFormat("<p><u>编号:</u><em>{0}</em></p>", item["ContractNumber"]);
// sb.AppendFormat("<p><u>合同名称:</u><em>{0}</em></p>", item["ContractName"]);
// sb.AppendFormat("<p><u>签约单位/个人:</u><em>{0}</em></p>", item["ContractUnit"]);
// sb.AppendFormat("<p><u>签约时间:</u><em>{0}</em></p>", Convert.ToDateTime(item["ContractTime"]).ToString("yyyy年MM月dd日 HH:mm:ss"));
// sb.AppendFormat("<p><u>合同类型:</u><em>{0}</em></p>", item["ContractTypeName"]);
// sb.AppendFormat("<p><u>责任社工:</u><em>{0}</em></p>", item["UserName"]);
// sb.Append("</span>");
// sb.Append("<section class=\"button\">");
// if (audit)
// {
// sb.AppendFormat("<a href=\"javascript:showDiv('{0}');\">审核</a>", item["ID"]);
// }
// sb.Append("</section></section>");
//});
小提示:如何对DataTable里面的行并行处理。
按照我们的理解,我们把一个参数给个可迭代的类型,第二个参数给个委托,
这样写视乎没毛病,第一个参数是个集合,然而显示错误,如果你基础好的话你应该知道,可以迭代的集合需要实现IEnumerable接口或声明GetEnumerator方法的类型,
我们dt.Rows返回的仅仅只是个集合类型,并没实现这两个条件中的一个。
有一个非常简单的方法可以知道你的集合适不适用与迭代,把你的集合打点 看下有没有 AsEnumerable() 此方法,有的话就可以。
把集合换成这个就可以了
dt.AsEnumerable()
更多转换问题就可以参数LinQ语法
使用并行类的任务并行
TPL还可以使用Parallel.Invoke()方法轻松触发多个异步任务.
列子:
点击下载按钮,从别的网站下载类容,然后在进行查询
string theBook;
private void btnDownload_Click(object sender, EventArgs e)
{
//从别的网站下载数据,并把获取的数据赋值给文本框
WebClient wc = new WebClient(); //这是System.Net里面的类
//这个完成事件自己声明
//wc.DownloadStringCompleted += wc_DownloadStringCompleted;
//利用委托来完成事件 效果一样
wc.DownloadStringCompleted += (s, eArg) =>
{
theBook = eArg.Result; //得到下载的数据
txtBook.Text = theBook;
}; wc.DownloadStringAsync(new Uri("http://www.gutenberg.org/files/98/98-8.txt")); //下载数据 不会阻止线程
} void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
theBook = e.Result;
txtBook.Text = theBook;
}
然后我们找出最长的单词和出现次数最多的10个单词
private void btnGetStatus_Click(object sender, EventArgs e)
{ //重电子书中获取单词
string[] words = theBook.Split(new char[] { ' ', '\u000A', ',', '.', ';', '-', '?', '/' }, StringSplitOptions.RemoveEmptyEntries); //找到最常用的个单词
string[] tenMostCommon = null; //= FindTenMostCommon(words);
//获取最长的单词
string longesWord = null;// = FindLongesWord(words); tenMostCommon = FindTenMostCommon(words);
longesWord = FindLongesWord(words);
StringBuilder sb = new StringBuilder("最常见的单词有:\n"); foreach (string item in tenMostCommon)
{
sb.AppendLine(item);
} sb.AppendFormat("最长的单词是:{0}", longesWord);
sb.AppendLine();
MessageBox.Show(sb.ToString(), "Book info");
} //查找出现次数前十单词
private string[] FindTenMostCommon(string[] words)
{
var freQuencyOrder = from word in words
where word.Length >
group word by word into g
orderby g.Count() descending
select g.Key;
string[] commonWords = (freQuencyOrder.Take()).ToArray();
return commonWords;
} //查找最长的单词
private string FindLongesWord(string[] words)
{
return (from word in words orderby word.Length descending select word).FirstOrDefault();
}
可以修改我买的方法,让应用程序使用所有计算机中可用的CUP,加快速度
//尽可能的同时执行这两个方法
Parallel.Invoke(() =>
{
tenMostCommon = FindTenMostCommon(words);
}, () =>
{
longesWord = FindLongesWord(words);
});
并行LINQ查询(PLINQ)
在System.Linq中的 ParallelEnumerable类提供了一扩展方法
定义:提供一组用于查询实现 ParallelQuery{TSource} 的对象的方法。 此命令的并行等效 Enumerable
它的扩展方法太多了,这里只写几个
例子:
private void btnExecute_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => //防止线程阻塞
{
ProcessInfoData();
});
} private void ProcessInfoData()
{ int[] soure = Enumerable.Range(, ).ToArray();//生成一个很大的数组
int[] modThreeIsZero = null; modThreeIsZero = (from num in soure where num % == orderby num descending select num).ToArray<int>();
MessageBox.Show(string.Format("found {0} numbers that query", modThreeIsZero.Count()));
}
使用PLINQ查询
改动一下代码,如果可以使用TPL并行的执行该查询,调用AsParallel()
modThreeIsZero = (from num in soure.AsParallel() where num % == orderby num descending select num).ToArray<int>();
取消PLINQ查询,跟上面的类似,把状态传过来就可以了,看一下完整版
private CancellationTokenSource cancelToken = new CancellationTokenSource();
private void btnExecute_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => //防止线程阻塞
{
ProcessInfoData();
});
} private void ProcessInfoData()
{ int[] soure = Enumerable.Range(, ).ToArray();//生成一个很大的数组
int[] modThreeIsZero = null;
try
{
modThreeIsZero = (from num in soure.AsParallel() where num % == orderby num descending select num).ToArray<int>();
MessageBox.Show(string.Format("found {0} numbers that query", modThreeIsZero.Count()));
}
catch (OperationCanceledException ex)
{ this.Invoke((Action)delegate
{
this.Text = ex.Message;
});
}
} private void btnCancel_Click(object sender, EventArgs e)
{
cancelToken.Cancel();
}
.NET 4.5 下的异步调用
注意啦,使用这个,你的版本需要到达4.5哦。
此版本新增了两个关键字,来简化了编写异步代码的过程。async和await关键字。
C#async和await关键字初深
C#async关键字用来指定某个方法、Lambda表达式或匿名方法自动以一部的方式来调用。在调用async方法时,await关键字自动暂停但前线程中任何其他活动,直到任务完成,离开调用线程。
例如:
private void btnCallMethod_Click(object sender, EventArgs e)
{
txtInput.Text = DoWorkAsync();
} private string DoWorkAsync()
{
Thread.Sleep();
return "Down with work!";
}
当我点击按钮的时候,需要等待10秒钟,文本框才能接受到类容,线程也阻塞了。用上面的方法实现起来需要写很多。但在.NET4.5下,我们可以这么写
在写之前,你要了解:
T代表返回的类型。
//async关键字修饰此方法
private async void btnCallMethod_Click(object sender, EventArgs e)
{
txtInput.Text = await DoWorkAsync(); //使用await接受类容 记住一点就可以了 async修饰了方法,里面一定要用await修饰Task.Run()
} private Task<string> DoWorkAsync()
{
//异步执行
var d= Task.Run(() =>
{
Thread.Sleep();
return "Down with work!";
});
//先谈框
MessageBox.Show(d.GetType().ToString());
return d;
}
此方法作为非阻塞调用。在被调用的方法名前面使用了await关键字。这很重要:如果async关键字修饰某个方法,但内部没有一个方法await方法调用,任会构建一个阻塞。
DoWork()的实现直接返回Task<T>对象,它是Task.Run()的返回值。 Task.Run( retun T:) Task,T>
异步方法的命名预定
任何返回Task的方法都用“Async”作为后缀。
返回void的异步方法
private async Task MethodAsync()
{
await Task.Run(() =>
{
Thread.Sleep();
});
}
单个async方法中可以拥有多个await上下文
private async void button2_Click(object sender, EventArgs e)
{
await Task.Run(() => { Thread.Sleep(); });
MessageBox.Show("我在做第一件事");
await Task.Run(() =>
{
Thread.Sleep();
});
MessageBox.Show("我在做第二件事"); await Task.Run(()=>{Thread.Sleep();});
MessageBox.Show("我在做第三件事");
}
执行了此方法,每等待两秒才会弹一次框,不会像上面那样先弹框。
关键点:
①方法(包括Lambda表达式和匿名方法)可以用async关键字标记,允许该方法以非阻塞的形式进行工作
②用async关键字标记的方法(包括Lambda表达式和匿名方法)在遇到await关键字之前将以阻塞的形式运行
③单个async方法可以拥有多个await上下文
④当遇到await表达式时,调用线程将挂起,直到await的任务完成。同时,控制将返回给方法的调用者(解释了为什么每等待2秒才弹框)
⑤await关键字将从视图中隐藏返回Task对象,直接返回实际的返回值。没有返回值的方法返回可以简单的返回void.
⑥根据命名预定,要被异步调用的方法应该以“Async”作为后缀
改进我们在System.Threading中的代码
//此方法不能用async标记
static void Main(string[] args)
{ AddAsync();
Console.ReadLine(); }
private static async Task AddAsync()
{
Console.WriteLine("开始你的表演:");
Console.WriteLine("ID of thread in Mian():{0}", Thread.CurrentThread.ManagedThreadId);
await Sum(, );
Console.WriteLine("其他的线程做完了");
} static async Task Sum(int a, int b)
{
await Task.Run(() =>
{
Console.WriteLine("ID of thread in Add():{0}",Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("{0}+{1} ={2}",a,b,a+b);
}); }
速度是相当的快。
基础也就介绍到这了。
System.Threading.Tasks的更多相关文章
- .Net多线程编程—System.Threading.Tasks.Parallel
System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Parallel.For,Parallel.ForEach这三个静态方法. 1 Parallel. ...
- using System.Threading.Tasks;
using System.Threading.Tasks; .Net并行库介绍——Task1
- HttpClient exception:ExceptionType:System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.IO.IOException: Unable to read data from the transport connection: Operation ca
error msg: System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System. ...
- 转载 Net多线程编程—System.Threading.Tasks.Parallel
.Net多线程编程—System.Threading.Tasks.Parallel System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Paral ...
- System.Threading.Tasks.Task 引起的 IIS 应用池崩溃
接口服务运行一段时间后,IIS应用池就会突然挂掉,事件查看日志,会有事件日志Event ID为5011的错误 为应用程序池“PokeIn”提供服务的进程在与 Windows Process Activ ...
- vs2013c#测试using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1_CXY { class Program { stati
首先安装Unit Test Generator.方法为:工具->扩展和更新->联机->搜索“图标为装有蓝色液体的小试管.Unit Test Generator”, 编写代码,生成一个 ...
- System.Threading.Tasks.Task引起的IIS应用程序池崩溃
问题现象 IIS应用程序池崩溃(Crash)的特征如下: 1. 从客户端看,浏览器一直处于连接状态,Web服务器无响应. 2. 从服务器端看(Windows Server 2008 + IIS 7.0 ...
- 一、并行编程 - 数据并行 System.Threading.Tasks.Parallel 类
一.并行概念 1.并行编程 在.NET 4中的并行编程是依赖Task Parallel Library(后面简称为TPL) 实现的.在TPL中,最基本的执行单元是task(中文可以理解为"任 ...
- System.Threading.Tasks.Task 任务引起的IIS应用程序池崩溃
转载:http://www.cnblogs.com/aaa6818162/p/4421305.html 问题现象 IIS应用程序池崩溃(Crash)的特征如下: 1. 从客户端看,浏览器一直处于连接状 ...
- System.Threading.Tasks并发和异步代码使用
main.cs System.Threading.Tasks.Parallel.For(0, 10, i => { TestLock test ...
随机推荐
- vue resource 携带cookie请求 vue cookie 跨域(六)
1.依赖VueResource 确保已安装vue-resource到项目中,找到当前项目,命令行输入: npm install vue-resource --save 在主方法添加 过滤 Vue.h ...
- Ranking relevance in yahoo search (2016)论文阅读
文章链接 https://www.kdd.org/kdd2016/papers/files/adf0361-yinA.pdf abstract 点击特征在长尾query上的稀疏性问题 基础相关性三大技 ...
- Java工具-检验ftp服务器的指定文件是否存在
项目工作中,需要检验ftp服务器中指定文件是否存在,在网上查阅了相关资料,可以通过ftpClient类进行实现. import org.apache.commons.net.ftp.FTP; impo ...
- 实现Web层的日志切面(方便清晰查看日志)
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.anno ...
- ASP.NET操作DataTable各种方法
转:http://www.cnblogs.com/isking/p/6178268.html http://www.cnblogs.com/sntetwt/p/3496477.html public ...
- Unity C# 使用JsonUtility读写Json文件
本文原创,转载请注明出处:http://www.cnblogs.com/AdvancePikachu/p/7146731.html 今天,为大家分享一下unity上的Json序列化,应该一说到这个词语 ...
- C#使用进度条,并用线程模拟真实数据 ProgressBar用法(转)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- poj 3162 树DP+单调队列
http://acm.hust.edu.cn/vjudge/problem/11552 http://blog.csdn.net/woshi250hua/article/details/7727677 ...
- <Android 基础(十七)> ViewPager介绍
介绍 Layout manager that allows the user to flip left and right through pages of data. You supply an i ...
- Android开发之——依赖冲突Program type already present
前言 实际开发中,为了提高开发速度和效率,总避免不了引用第三方提供的依赖和类库,如果含有相同依赖的类库被我们引用时,而他们的版本又不相同,就有可能会导致一系列问题和异常,本文结合本人时间总结和他人经验 ...