线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一.通过委托的方式
委托中的Invoke方法完成一个委托方法的封送,在Invoke封送的方法被执行完毕前,Invoke方法不会返回,从而调用者线程将被阻塞。委托中的BeginInvoke方法是从ThreadPool取出一个线程来执行这个方法,以获得异步执行效果的。
public delegate int AddDelegate(int a, int b); //定义一个委托对象
static void Main(string[] args)
{
Console.WriteLine("start");
AddDelegate d1 = Add;
//异步回调方法,异步方法执行完可以选择执行这个方法
AsyncCallback cab = t =>
{
//Console.WriteLine(d1.EndInvoke(t));
Console.WriteLine(t.AsyncState); //可以获取传递过来的参数
Console.WriteLine(t.IsCompleted);
};
IAsyncResult ar = d1.BeginInvoke(5,2,cab,12); //前两个参数为引用方法传递的参数,cab为上面的回调方法,12为回调方法中的参数,执行BeginInvoke会返回一个IAsyncResult对象
Thread.Sleep(2000);
int result = d1.EndInvoke(ar); //该方法返回委方法中的真正返回值,该方法会阻塞主线程一直到异步执行完,EndInvoke只能被执行一次
Console.WriteLine("结果:"+result);
}
public static int Add(int a,int b)
{
return a + b;
}
二.通过Thread类
C#中的Thread类主要提供四个构造方法,ThreadStart是一个无参的委托,意味着我们能传递一个无参的方法进去。ParameterizedThreadStart是一个只有一个参数,参数类型为object的委托,我们可以传一个一个参数,参数类型为object的类型进去
static void Main(string[] args)
{
//ThreadStart start1 = AddNoParms;
//Thread thread1 = new Thread(start1);
Thread thread1 = new Thread(AddNoParms); //简化写法,直接传递与委托方法签名对应的方法
Thread thread2 = new Thread(AddHasParms); //有参的形式
thread1.Start();//开启这个线程
thread2.Start("thread2"); //开启有参的Thread线程
thread1.Name = "thread1"; //获取或设置线程的名称。
thread1.IsBackground = true; //获取或设置一个值,该值指示某个线程是否为后台线程。
Thread.Sleep(2000); //让线程暂停一段时间。
thread1.Abort(); //销毁线程
Console.WriteLine(thread1.IsAlive); //获取线程状态
//获取异步回调的返回值
int returndata = 0; //声明一个变量用来接收异步方法执行的返回值
ThreadStart threadstart = new ThreadStart
(() =>{
returndata = Add(1, 2);
AddNoParms();
});
Thread thread3 = new Thread(threadstart);
thread3.Start();
Console.WriteLine(returndata);
}
public static void AddHasParms(object str)
{
Console.WriteLine(str);
}
public static void AddNoParms()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("add");
}
public static int Add(int a,int b)
{
return a + b;
}
Thread类中还提供了许多属性和方法供我们使用
//属性
IsAlive //获取一个值,该值指示当前线程的执行状态。
IsBackground //获取或设置一个值,该值指示某个线程是否为后台线程。前台进程跟后台进程的区别:程序关闭时,后台线程直接关闭,但前台线程会执行完后关闭。通过Thread类新建线程默认为前台线程。其他方式创建的都是后台线程。
IsThreadPoolThread //获取一个值,该值指示线程是否属于托管线程池。
Priority //获取或设置一个值,该值指示线程的调度优先级。
//方法
Abort //在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。
Join //阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式
ResetAbort //取消为当前线程请求的 Abort。
三.ThreadPool
ThreadPool类会在线程的托管池中重用已有的线程,一个应用程序最多只能有一个线程池,可以通过QueueUserWorkItem方法将工作函数排入进程池。QueueUserWorkItem有两个构造函数,WaitCallback是一个只有一个参数,参数类型为object的委托,我们可以传一个参数,参数类型为object的对象进行实例化
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1,1);
ThreadPool.SetMaxThreads(1,5);
//调用方式一 用WaitCallback这个委托进行实例化
WaitCallback waitCallback = new WaitCallback(AddHasParms);
ThreadPool.QueueUserWorkItem(waitCallback, "helloworld");
//调用方式二 传递与WaitCallback对应的方法进行实例化
ThreadPool.QueueUserWorkItem(AddHasParms,"helloworld");
//调用方法三 通过WaitCallback相对应的匿名方法,在匿名方法中进行调用
ThreadPool.QueueUserWorkItem(n=> {
AddNoParms();
AddHasParms("helloworld");
});
}
public static void AddHasParms(object str)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(str);
}
public static void AddNoParms()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
四.Task
static void Main(string[] args)
{
//方式一 无返回值时
Task task1 = new Task(AddHasParms, "helloworld");
task1.Start();
Task.WaitAll(task1); //等待所有任务结束
//有返回值时
Task<int> task2 = new Task<int>(Add, "obj");
task2.Start();
Console.WriteLine(task2.Result); //打印返回值
//方式二 通过Run方法创建 无返回值
Task.Run(() => AddHasParms("hello"));
//无返回值时
Task<int> task3 = Task.Run(() => Add("obj"));
Console.WriteLine(task3.Result);
//方式三 无返回值时
Task.Factory.StartNew(() => AddHasParms("helloworld"));
Task.WaitAll();
//有返回值时
var task4=Task.Factory.StartNew(() => Add("obj"));
Console.WriteLine(task4.Result);
}
public static void AddHasParms(object str)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(str);
}
public static int Add(object a)
{
return 1;
}