文章目录
前言
多线程简单来说就是让计算机进行“一心多用”,不过此处一心多用并不是真正的一心多用,只是在不同的时间片段(极短的时间片段内)分别执行不同的操作,但在单位时间片段内饰一心一用。简单举个例子,周伯通让小龙女学习左右互搏,让她一手画方一手画圆,做到一心两用。那我们来简单分析一下,如果不是同时结束呢,我们先这样来,左手先画四分之一圆,完成后右手再画正方形的一条边,然后依次类推,知道方圆均画完。这样属于两个手分别操作,那么我们进一步细分,左后先画八分之一圆,右手再画八分之一正方形,当然这样还是两个手分别操作,那么我们整个圆和方形分成一百步画完呢,假如分成一千步呢?分成一千步即使每一步都是单独的在画圆或者或方,但是总体上方圆基本同时在进行。这就是多线程所展现给我们的,但是多线程的意义就是当某一线程阻塞了,不会导致整个程序等待。加入我们只会用一只手画方圆,先画圆后画方,那么在画圆的过程中加入手抽筋了,那我们都要在等待手恢复,如果一手画方一手画圆,那么左手即使抽筋了,只是影响画圆而不影响右手画方。这就是多线程的意义。下面讲解三种最基本的开启线程方法,讲解过程中为了分别讲解,只引用了部分代码,最后会给出完整代码,建议可以边运行边看分步讲解。
1.基本方法
引入命名空间System.Threading,最基本在简洁的方法ThreadStart/ParameterizedThreadStart,前者不带参数,后者可以给方法传递一个参数(带参数的方法入口参数必须为object类型),但是最基本的方法,开启线程比较随意,容易混乱而且开启过多线程也会影响性能。开启线程不过是在主程序运行或者其他方法运行时,开启其他方法,既然开启其他就必然用得到委托,本质上还是向thread类中传递要开启的方法,具体开启线程有thread类完成。
1.0 子线程运行的方法
static void AsyncDemo()//无参数的方法
{
for(int i=0;i<=10;i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
static void AsyncDemo(object obj)//有参数的方法
{
string _obj = (string)obj;
Console.WriteLine(_obj);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
1.1 开启线程
//Thread thread = new Thread(new ThreadStart(AsyncDemo));
//thread.Start();
string para = "Hello World!";
Thread thread = new Thread(new ParameterizedThreadStart(AsyncDemo));
thread.Start(para);
1.2 主程序运行内容
//主程序
for (int i=0;i<=10;i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
本例子通过两个for语句来分别假定主程序方法和线程方法,如果不用线程,会一个循环执行完在执行另一个循环,如果没有Thread.Sleep(100)则体现不出同时运行的效果,因为就是在极短的时间片段内,每一个循环都可以执行完,所以为了达到效果,让每一个都暂停0.1s。
2.线程池
在程序中随意开启线程会影响程序的质量,所以C#提供了线程池来管理线程,即通过QueueUserWorkItem来开启线程,他有两个重载,可以传递参数给开启线程的方法。开启线程的方法还是采用1、中方法,线程开启如下:
string str = "Hello World!";
ThreadPool.SetMaxThreads(1000, 1000);//定义处于活动状态的线程池的请求数目
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo),str);
// ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo));//无参数的方法
3.带返回值方法
以上两种方法都没有返回值,若一个方法有返回值,则需要其他方法来实现。开启线程本质上还是通过委托传递一个方法(函数),而委托也为我们提供了相应开启线程的方法。当我们定义好委托以后,在码代码的时候平台会自动给我们提示它可能存在的方法,其中BeginInvoke(name, null, null)方法就是来开启线程,第一个参数为方法所需要的入口参数,第二个为回调函数,其参数必须为一个IAsyncResult 接口,第三个为 为回调函数传入的参数。后两个参数可以为空,它返回一个IAsyncResult类型的接口,通过此接口传入EndInvoke(result)方法即可获取返回值。下面为具体步骤:
3.0 建立委托
public delegate string Mydeletegate(string str);
3.1 定义相关方法
static string AsyncDemo(string obj)
{
string _obj = (string)obj;
Console.WriteLine(_obj);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
return "Hello " + obj;
}
3.2 委托指向方法并开启线程
IAsyncResult result = de.BeginInvoke(name, null, null);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
string redata = de.EndInvoke(result);
Console.WriteLine(redata);
但是此方法会存在一个问题,即如果把上述代码中的redata那一语句放在for循环之前,则会先执行型委托指向的方法的循环,后执行主程序(可以自行测试),因为主程序在等待执行完redata=。。。。后再执行for语句,所以可以充分利用接口IAsyncResult提供的方法或者字段。也可以采用BeginInvoke方法的第二个参数,回调函数,即把EndInvoke方法放入回调函数中,本例的回调函数为Completed。回调函数如下
static void Completed(IAsyncResult res)
{
string str = (string)res.AsyncState;
AsyncResult _res = (AsyncResult)res;
Mydeletegate de1 = (Mydeletegate)_res.AsyncDelegate;
string data = de1.EndInvoke(res);
Console.WriteLine(data);
Console.WriteLine(str);
}
对相关参数进行一下解释:通过接口中的AsyncState可以获取异步操作传入的信息,即BeginInvoke的第三个参数,为object类型的,要转化成所用的类型。我们要在此函数中完成EndInvoke就需要获取BeginInvoke的委托,查看元数据可以知道AsyncResult 继承了IAsyncResult 接口,AsyncResult中的AsyncDelegate属性可以获取相关委托,所以先把传入的IAsyncResult 转化为AsyncResult 类型,在通过AsyncDelegate获取到委托(其为object类型,需要转化为委托类型),然后调用EndInvoke方法,获取返回值, 主程序如下:
de.BeginInvoke(name, new AsyncCallback(Completed), index);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
在最后附上三种方法的完整程序,这三种方法为最基本的方法,在以后不管是网络请求还是文件I/O的异步编程都与委托类方法一样。
4.完整代码
4.1 基本方法代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MyThread
{
class Program
{
static void AsyncDemo()//无参数的方法
{
for(int i=0;i<=10;i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
static void AsyncDemo(object obj)//有参数的方法
{
string _obj = (string)obj;
Console.WriteLine(_obj);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
static void Main(string[] args)
{
//无参数方法
//Thread thread = new Thread(new ThreadStart(AsyncDemo));
//thread.Start();
string para = "Hello World!";
Thread thread = new Thread(new ParameterizedThreadStart(AsyncDemo));
thread.Start(para);
//主程序
for (int i=0;i<=10;i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
Console.ReadKey();
}
}
}
4.2 线程池代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadPoolTest
{
class Program
{
static void AsyncDemo()
{
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
static void AsyncDemo(object obj)
{
string _obj = (string)obj;
Console.WriteLine(_obj);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
}
static void Main(string[] args)
{
string str = "Hello World!";
ThreadPool.SetMaxThreads(1000, 1000);//定义处于活动状态的线程池的请求数目
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo),str);
// ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo));//无参数的方法
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
Console.ReadKey();
}
}
}
4.3 返回值线程
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Deletegatetest
{
public delegate string Mydeletegate(string str);
class Program
{
static string AsyncDemo(string obj)
{
string _obj = (string)obj;
Console.WriteLine(_obj);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async thread : " + i);
}
return "Hello " + obj;
}
//回调函数
static void Completed(IAsyncResult res)
{
string str = (string)res.AsyncState;
AsyncResult _res = (AsyncResult)res;
Mydeletegate de1 = (Mydeletegate)_res.AsyncDelegate;
string data = de1.EndInvoke(res);
Console.WriteLine(data);
Console.WriteLine(str);
}
static void Main(string[] args)
{
string name = "Andy";
string index = "Index!";
Mydeletegate de = new Mydeletegate(AsyncDemo);
//IAsyncResult result = de.BeginInvoke(name, null, null);
de.BeginInvoke(name, new AsyncCallback(Completed), index);
for (int i = 0; i <= 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("Main thread : " + i);
}
//string redata = de.EndInvoke(result);
//Console.WriteLine(redata);
//for (int i = 0; i <= 10; i++)
//{
// Thread.Sleep(100);
// Console.WriteLine("Main thread : " + i);
//}
Console.ReadKey();
}
}
}