在C#4.0以后,可以使用System.Threading.Tasks下的Task类来创建和管理多线程,下面将记录如何简单的使用Task.
一、使用Task执行静态方法
在控制台编写如下下面:
static void Main(string[] args)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid);
Task.Run(() => PrintName("小明"));
Console.ReadKey();
}
private static void PrintName(string name)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
}
如上代码即展示了最简单的创建一个线程执行一个方法的过程,从执行结果可以看出主线程和Task创建的线程。
二、使用Task执行非静态方法
Task创建一个线程执行静态方法固然简单,但是不能用同样的方法执行非静态方法,是因为非静态方法所属的类必须要实例化,所以Task创建线程执行非静态方法有点区别,如下代码将展示如何编写代码:
1.在控制台中新增一个方法:
public void PrintName2(string name)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
}
2.控制台Main方法中编写如下代码:
static void Main(string[] args)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid);
Task.Run(() => PrintName("小明"));
Task task = new Task(() => new Program().PrintName2("小花"));
task.Start();
Console.ReadKey();
}
注意:Task执行非静态方法必须需要实例化非静态方法的类,并且调用Task的Start方法才能正确执行。
三、Task执行具有返回值的方法
上述代码中展示了如何创建线程执行静态方法和非静态方法,并且传递了一个参数,那么下面将展示如何执行带有返回值的方法:
1.将如上的PrintName代码更改成带有返回值的方法,如下:
private static string PrintName(string name)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
return name;
}
2.在控制台中使用Result即可获得返回值,即
var task = Task.Run(() => PrintName("小明"))
Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid + "返回值:" + task.Result);
3.假如返回的是一个结构或者类呢?
1)创建一个Person类,代码如下:
public class Person
{
private string _name = "";
public void SetName(string name)
{
_name = name;
}
public string GetName()
{
return _name;
}
}
2)将控制台中的PrintName更改成返回一个Person对象,代码如下:
private static Person PrintName(string name)
{
Person person = new Person();
person.SetName(name);
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
return person;
}
3)控制台中这样编写即可:
var task = Task
并且此时task就是一个Person对象,直接可以使用如task.Result.GetName()来获取对象的方法。
四、TaskFactory使用范例
在System.Threading.Tasks命名空间下有一个TaskFactory类,使用该类同样可以创建现在执行方法,注意的是,这个类可以单独使用也可以在Task类下使用,两种使用方法如下:
Task.Factory.StartNew(()=> PrintName("小王"));
TaskFactory factory = new TaskFactory();
factory.StartNew(() => PrintName("小张"));
factory.StartNew(() => PrintName("小赵"));
五、高频率多次使用Task创建线程执行方法注意事项
假如如下代码循环多次执行PrintName方法,将会出现什么情况呢?
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Task.Run(() => PrintName("小明" + i));
}
Console.ReadKey();
}
private static void PrintName(string name)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
}
运行结果如图:
从结果看,很明显不是控制台中代码的意图,控制台中代码的意图是想,每次循环希望调用的方法的参数将循环变量添加进去,为何结果是这样的呢?假如需要达到真实的意图,即把代码改成:
string name = "小明" + i;
Task.Run(() => PrintName(name));
这样既可,那么为何不能直接在Task执行的方法参数中拼接,两种拼接的区别又在哪?
1.string name = "小明" + i;这样的编写,每次传递的是一个固定的字符串的引用的值,所以运行结果就是每次执行方法传递的参数值是固定且不同的。
2.Task.Run(() => PrintName("小明" + i));这种写法是,每次都传递了一个变量,这个变量的在Task执行方法的时候的值并不是循环体循环时候的值,本次执行结果都是10,是因为for循环极快,10次都循环完了,第一个Task的方法还没执行完毕,假如调试加个断点或者阻塞主进程一会,输出的将不会都是10.
综上所述,在Task创建线程执行方法时候,假如方法有参数,参数一定不能是变量,否则结果会事与愿违。