C#异步多线程的实现

  • Post author:
  • Post category:其他




同步与异步的区别

异步是基于多线程的,一个操作再执行时不会阻止其他线程的工作。

1、同步方法的所有计算都在主线程进行,可能会卡UI界面;异步方法的所有计算都在子线程中,不会卡界面

2、异步是多线程的,对于CPU的利用率高,资源耗费多;同步方法对于CPU利用率低



异步的实现方式



1、delegate委托的异步调用

步骤:

1、定义同步方法

public void Func();

2、定义类型与同步方法一致的委托

public delegate void FuncDelegate();

3、定义委托变量,与同步方法进行绑定

FuncDelegate delegate=new FuncDelegate(Func);

4、调用委托的BeginInvoke()和EndInvoke(),从而实现异步调用同步方法的效果

delegate.BeginInvoke(null,null); //无回调函数则为null

对于无参无返回值和有参无返回值两种情况,直接调用委托的BeginInvoke方法。



a)BeginInvoke

public IAsyncResult BeginInvoke(int p1,out double p2,ref bool p3,AsyncCallBack callback,object AsyncState)

IAsyncResult:返回的接口、监控

p1/p2/p3:同步方法的参数

callback:回调函数

AsyncState:传给回调函数的参数



b)CallBack:BeginInvoke执行结束会自动执行回调函数

public static void CallBackMethod(IAsyncResult ia){}

ia.AsyncState :表示传递给回调函数的参数

ia.AsyncWaitHandle.WaitOne(int mms): 线程等待mms毫秒以便异步方法执行完成

ia.AsyncDelegate: 获得委托的返回值

public static void CallBackMethod(IAsyncResult ia)
{
	AsyncResult asyncresult=(AsyncResult)ia;
	//获取回调函数的参数
	string parameter=asyncresult.AsyncState.ToString();
	//获取异步调用的委托
	FuncDelegate delegate=(FuncDelegate)asyncresult.AsyncDelegate;
	int re=delegate.EndInvoke(ia);
}



c)EndInvoke:在线程池种清理完成的异步方法

public 返回值 EndInvoke(out double p2,ref bool p3,IAsyncResult ia)

返回值:异步方法的返回值

p2/p3:只传入out参数

ia:BeginInvoke的返回值


说明:


如果调用异步方法的主线程需要获取异步的结果或等待异步方法的完成,那么可以在主调函数种调用EndInvoke(会阻塞主线程,卡界面)

如果不需要异步方法的结果,不需要等异步方法执行结束,可以在回调函数中调用EndInvoke(不会阻塞主线程,不会卡界面)


例子

//主线程
public void MainFunc()
{
	//无参无返回值
	delegate.BeginInvoke(null,null);
	//有参无返回值
	 delegate.BeginInvoke(18,"Tom",null,null);
	 //无参有返回值
	 IAsyncResult ia=delegate.BeginInvoke(null,null);        
	 ia.AsyncWaitHandle.WaitOne(1000);     //等待一定时间以便异步执行完毕
	 int re=delegate.EndInvoke(ia);      //得到异步程序的返回值,   获取子线程的返回值会阻塞主线程(在回调函数中调用不会阻塞主线程)
	 //有参有返回值
	 IAsyncResult ia=delegate.BeginInvoke(18,"Tom",null,null);
	 int re=delegate.EndInvoke(ia);
	 //有回调函数,有回调函数的参数
	 IAsyncResult ia=delegate.BeginInvoke(CallBackMethod,asyncState);
}



2、Thread

默认不支持返回值,不支持回调函数

public void MainFunc()
{
	//无参无返回值
	ThreadStart threadstart=new ThreadStart(Func);      //Func同步方法
	Thread thread=new Thread(threadstart);
	thread.Start();
	//有参无返回值
	ParameterizedThreadStart threadstart=new ParameterizedThreadStart(Func);   //Func有参无返回值,参数必须是object类型
	Thread thread=new Thread(threadstart);
	thread.Start(18);
	//无参有返回值
	Func<int> func=new Func<int>(Func);           //1、定义一个有返回值的委托,绑定同步方法
	Func<int> begininvoke=BeginInvoke<int>(func);    //2、定义一个参数是委托,返回委托的BeginInvoke泛函,内部用Thread实现
	int re=begininvoke.Invoke();                            //3、Invoke同步实现   ,会阻塞主线程
	//有参有返回值
	Func<int,string,int> func=new Func<int,string,int>(func);     //1、func是有两个参数,一个int返回值的同步方法
	Func<int> begininvoke=BeginInvoke<int>(func);                  //2、BeginInvoke是参数和返回值均为委托的泛函,内部用Thread实现
	int re=begininvoke.Invoke();                                                  //3、同步实现
}

public Func<T> BeginInvoke<T>(Func<T> func)
{
	T t=default(T);
	ThreadStart thread=new ThreadStart(()=>
	{
		func.Invoke();
	})
	Thread thread=new Thread(threadstart);
	thread.Start();
	return new Func<T>(()=>
	{
		thread.Join();
		return t;
	})
}


注意:


方法中的委托和同步方法不一定相同,根据具体情况改变参数和返回值。

如果需要回调函数,则在BeginInvoke方法中加入回调函数的调用

Func.Invoke();

CallBackMethod.Invoke();



3、Task

public void MainFunc()
{
	//Task类创建task             Start
	Task task=new Task(Func);
	task.Start();
	
	//TaskFactory类创建task并自动启动            StartNew
	TaskFactory taskfactory=new TaskFactory();
	Task task=taskfactory.StartNew(Func);
	
	//Task类创建task并自动启动	                Run
	Task task=Task.Run(Func);
	
	//ContinueWith    不会阻塞主线程
	Task task1=new Task(Func);
	task1.ContinueWith((a)=>                //创建一个task1执行完之后继续执行的任务
	{
		...
	})
	
	//ContinueWhenAny 不会阻塞主线程
	TaskFactory taskfactory=new TaskFactory();
	Task task1=taskfactory.StartNew(()=>{...});
	Task task2=taskfactory.StartNew(()=>{...});
	Task task3=taskfactory.StartNew(()=>{...});
	taskfactory.ContinueWhenAny(new Task[]{task1,task2,task3},(a)=>{...});    //创建一个任务,在任务组中任意一个任务完成之后开始执行
	
	//ContinueWhenAll   不会阻塞主线程
	taskfactory.ContinueWhenAll(new Task[]{task1,task2,task3},(a)=>{...});    //创建一个任务,在任务组中所有任务完成之后开始执行
	
	//WaitAny   会阻塞主线程
	Task.WaitAny(new Task[]{task1,task2,task3});     //等待任务组中任意一个任务完成开始往下执行
	//将任务组和WaitAny放到Task中可以解决阻塞主线程的问题

	//WaitAll    会阻塞主线程
	Task.WaitAll(new Task[]{task1,task2,task3});       //等待任务组中所有任务完成之后开始往下执行
	//将任务组和WaitAll放到Task中可以解决阻塞主线程的问题
	
	//WhenAny  不会阻塞主线程
	Task task=Task.WhenAny(new Task[]{task1,task2,task3});
	task.ContinueWith((a)=>{...});

	//WhenAll   不会阻塞主线程
	Task task=Task.WhenAll(new Task[]{task1,task2,task3});
	task.ContinueWith(()=>{...});

	//有参数有返回值      会阻塞主线程
	Task<int> task=new Task<int>(new Func<object,int>((a)=>{...}),"aaa");
	task.Start();
	int re=task.Result;
	//需要获取结果时将其放到task中异步执行可以解决阻塞主线程的问题
}



4、ThreadPool

public void MainFunc()
{
	//QueueUserWorkItem  不会阻塞主线程  多个工作项是异步执行的
	ThreadPool.QueueUserWorkItem(new WaitCallBack((a)=>{...}));
	ThreadPool.QueueUserWorkItem(new WaitCallBack((b)=>{}),"bbb");  //可以传递一个object类型的参数
}



5、Parallel

public void MainFunc()
{
	//For  会阻塞主线程  3个线程异步执行,执行完之后才会开启新的3个线程
	ParallelOptions options=new ParallelOptions();
	options.MaxDegreeOfParallelism=3;
	Parallel.For(1,6,options,(a,loopState)=>{
		...
	});

	//Foreach  会阻塞主线程  3个线程异步执行,执行完3个之后才会开启新的3个线程
	Parallel.Foreach(int[]{1,2,3,4,5},options,(a,loopState)=>{
		...
	})

	//Invoke  会阻塞主线程  3个线程异步执行,执行完1个开始执行新的任务,保证3个同时进行
	Parallel.Invoke(options,
	()=>{...},
	()=>{...},
	()=>{...},
	()=>{...}
	);
}



6、async&await

public void MainFunc()
{
	AsyncFunc();             //异步方法,内部有await
}

public Async void AsyncFunc()
{
	Task task1=new Task(()=>{...});
	await task1; 
	//遇到await之后开启新线程,主线程继续执行主函数后面的程序
	//AsyncFunc往后的内容只能等task1执行完之后才能继续,且由主线程接手
	Task task2=new Task(()=>{...});
	await task2;
}
```



版权声明:本文为angelsha417原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。