이 글에서는 C#의 Thread, Task, Async/Await, IAsyncResult 관련 지식을 주로 소개합니다. 특정 참조 값이 있으므로 아래 편집기를 통해 살펴보겠습니다
비동기, 스레드, 태스크, async/await, IAsyncResult에 대해 말하자면 오늘은 확실히 피할 수 없는 것들에 대해 차례로 이야기하겠습니다
1. 스레드
멀티 스레딩의 의미는 애플리케이션에서 더 많은 시간이 소요되는 작업을 위해 여러 실행 부분을 동시에 실행할 수 있다는 것입니다. , io, 데이터베이스 작업 ) 또는 응답을 기다리는 중(예: WCF 통신) 백그라운드 스레드를 별도로 시작하여 작업을 수행할 수 있으므로 메인 스레드가 차단되지 않고 계속 실행할 수 있습니다. 백그라운드 스레드가 완료될 때까지 기다린 후 메인 스레드에 알리고 해당 작업을 수행합니다.
C#에서는
static void Main(string[] args) { Console.WriteLine("主线程开始"); //IsBackground=true,将其设置为后台线程 Thread t = new Thread(Run) { IsBackground = true }; t.Start(); Console.WriteLine("主线程在做其他的事!"); //主线程结束,后台线程会自动结束,不管有没有执行完成 //Thread.Sleep(300); Thread.Sleep(1500); Console.WriteLine("主线程结束"); } static void Run() { Thread.Sleep(700); Console.WriteLine("这是后台线程调用"); }
에서 새 스레드를 시작하는 것이 비교적 간단합니다. 실행 결과는 다음과 같습니다.
백그라운드 스레드가 시작된 후에도 메인 스레드가 계속 실행되는 것을 볼 수 있습니다.
1.1 스레드 풀
웹사이트 백그라운드 등 처리해야 할 작업이 많다고 상상해 보세요. HTTP 요청 처리를 위해 각 요청마다 백그라운드 스레드를 생성해야 하는 것은 당연합니다. 메모리를 많이 차지하며, 잦은 생성 프로세스도 속도에 심각한 영향을 미치게 되는데, 이 문제를 해결하려면 생성된 스레드를 저장하여 스레드 풀을 구성해야 합니다. 작업을 처리할 때 스레드 풀에 유휴 스레드가 있는 경우(이전 작업이 완료된 후 스레드는 재활용되지 않고 유휴 상태로 설정됨) 스레드 실행을 직접 호출합니다. 스레드 풀(예: asp.net 처리 메커니즘의 Application 개체 ),
사용 예:
for (int i = 0; i < 10; i++) { ThreadPool.QueueUserWorkItem(m => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString()); }); } Console.Read();
실행 결과 :
10번을 실행했는데도 10개의 스레드가 생성되지 않은 것을 볼 수 있습니다. 🎜>
1.2 세마포 Semaphore는 스레드 조정을 담당하며 특정 리소스에 액세스하는 스레드 수를 제한할 수 있습니다.
다음은 SemaphoreSlim에 대한 소개입니다. 클래스 사용의 간단한 예:
static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问 static void Main(string[] args) { for (int i = 0; i < 10; i++) { new Thread(SemaphoreTest).Start(); } Console.Read(); } static void SemaphoreTest() { semLim.Wait(); Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "开始执行"); Thread.Sleep(2000); Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "执行完毕"); semLim.Release(); }
실행 결과는 다음과 같습니다.
보시다시피 처음에는 스레드가 3개만 실행되고 스레드가 실행되고 해제되면 새로운 스레드가 생성됩니다.
SemaphoreSlim 클래스 외에도 좀 더 유연한 느낌의 Semaphore 클래스를 사용할 수도 있으니 관심이 있으시면 검색하시면 됩니다!
2.TaskTask는 .NET4.0에 추가되었습니다. Task를 사용하여 새 작업을 시작하면, 스레드 풀에서 그려지며 스레드는 인스턴스화될 때마다 새 스레드를 생성합니다.
Console.WriteLine("主线程启动"); //Task.Run启动一个线程 //Task启动的是后台线程,要在主线程中等待后台线程执行完毕,可以调用Wait方法 //Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); }); Task task = Task.Run(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); }); Thread.Sleep(300); task.Wait(); Console.WriteLine("主线程结束");
실행 결과는 다음과 같습니다.
새 작업을 시작하는 방법: Task.Run() 또는 Task.Factory.StartNew(), 백그라운드를 시작하는 스레드
가 백그라운드 스레드가 실행을 완료할 때까지 기본 스레드에서 기다리려는 경우 Wait 메서드를 사용할 수 있습니다(동기 방식으로 실행됩니다). Wait가 없으면 비동기적으로 실행됩니다.
Task와 Thread 비교:
static void Main(string[] args) { for (int i = 0; i < 5; i++) { new Thread(Run1).Start(); } for (int i = 0; i < 5; i++) { Task.Run(() => { Run2(); }); } } static void Run1() { Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId); } static void Run2() { Console.WriteLine("Task调用的Thread Id =" + Thread.CurrentThread.ManagedThreadId); }
실행 결과:
Thread를 직접 사용하면 5개의 Thread가 열리는 것을 볼 수 있습니다. 작업(스레드 풀 사용) 열기 3!
2.1 Task
Console.WriteLine("主线程开始"); //返回值类型为string Task<string> task = Task<string>.Run(() => { Thread.Sleep(2000); return Thread.CurrentThread.ManagedThreadId.ToString(); }); //会等到task执行完毕才会输出; Console.WriteLine(task.Result); Console.WriteLine("主线程结束");
실행 결과:
task.Result를 통해 반환 값을 얻을 수 있습니다. 값을 얻었을 때 백그라운드 스레드의 실행이 완료되지 않은 경우, 완료될 때까지 기다리세요!
간단한 언급:
CancellationTokenSource 클래스를 통해 작업을 취소할 수 있으며 많이 사용되지 않고 사용법도 비교적 간단하다고 생각됩니다. !
3. async/awaitasync/await는 C# 5.0에 도입되었으며 먼저 사용법은 다음과 같습니다.
static void Main(string[] args) { Console.WriteLine("-------主线程启动-------"); Task<int> task = GetStrLengthAsync(); Console.WriteLine("主线程继续执行"); Console.WriteLine("Task返回的值" + task.Result); Console.WriteLine("-------主线程结束-------"); } static async Task<int> GetStrLengthAsync() { Console.WriteLine("GetStrLengthAsync方法开始执行"); //此处返回的<string>中的字符串类型,而不是Task<string> string str = await GetString(); Console.WriteLine("GetStrLengthAsync方法执行结束"); return str.Length; } static Task<string> GetString() { //Console.WriteLine("GetString方法开始执行") return Task<string>.Run(() => { Thread.Sleep(2000); return "GetString的返回值"; }); }
async는 메서드를 수정하는 데 사용됩니다. 이는 이 메서드가 비동기식임을 나타내며 선언된 메서드의 반환 유형은 void, Task 또는 Task
await必须用来修饰Task或Task
看看运行结果:
可以看出来,main函数调用GetStrLengthAsync方法后,在await之前,都是同步执行的,直到遇到await关键字,main函数才返回继续执行。
那么是否是在遇到await关键字的时候程序自动开启了一个后台线程去执行GetString方法呢?
现在把GetString方法中的那行注释加上,运行的结果是:
大家可以看到,在遇到await关键字后,没有继续执行GetStrLengthAsync方法后面的操作,也没有马上反回到main函数中,而是执行了GetString的第一行,以此可以判断await这里并没有开启新的线程去执行GetString方法,而是以同步的方式让GetString方法执行,等到执行到GetString方法中的Task
那么await的作用是什么呢?
可以从字面上理解,上面提到task.wait可以让主线程等待后台线程执行完毕,await和wait类似,同样是等待,等待Task
那么await是怎么做到的呢?有没有开启新线程去等待?
只有两个线程(主线程和Task开启的线程)!至于怎么做到的(我也不知道......>_<),大家有兴趣的话研究下吧!
4.IAsyncResult
IAsyncResult自.NET1.1起就有了,包含可异步操作的方法的类需要实现它,Task类就实现了该接口
在不借助于Task的情况下怎么实现异步呢?
class Program { static void Main(string[] args) { Console.WriteLine("主程序开始--------------------"); int threadId; AsyncDemo ad = new AsyncDemo(); AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod); IAsyncResult result = caller.BeginInvoke(3000,out threadId, null, null); Thread.Sleep(0); Console.WriteLine("主线程线程 {0} 正在运行.",Thread.CurrentThread.ManagedThreadId) //会阻塞线程,直到后台线程执行完毕之后,才会往下执行 result.AsyncWaitHandle.WaitOne(); Console.WriteLine("主程序在做一些事情!!!"); //获取异步执行的结果 string returnValue = caller.EndInvoke(out threadId, result); //释放资源 result.AsyncWaitHandle.Close(); Console.WriteLine("主程序结束--------------------"); Console.Read(); } } public class AsyncDemo { //供后台线程执行的方法 public string TestMethod(int callDuration, out int threadId) { Console.WriteLine("测试方法开始执行."); Thread.Sleep(callDuration); threadId = Thread.CurrentThread.ManagedThreadId; return String.Format("测试方法执行的时间 {0}.", callDuration.ToString()); } } public delegate string AsyncMethodCaller(int callDuration, out int threadId);
关键步骤就是红色字体的部分,运行结果:
和Task的用法差异不是很大!result.AsyncWaitHandle.WaitOne()就类似Task的Wait。
5.Parallel
最后说一下在循环中开启多线程的简单方法:
Stopwatch watch1 = new Stopwatch(); watch1.Start(); for (int i = 1; i <= 10; i++) { Console.Write(i + ","); Thread.Sleep(1000); } watch1.Stop(); Console.WriteLine(watch1.Elapsed); Stopwatch watch2 = new Stopwatch(); watch2.Start(); //会调用线程池中的线程 Parallel.For(1, 11, i => { Console.WriteLine(i + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); }); watch2.Stop(); Console.WriteLine(watch2.Elapsed);
运行结果:
循环List
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 }; Parallel.ForEach<int>(list, n => { Console.WriteLine(n); Thread.Sleep(1000); });
Action[] actions = new Action[] { new Action(()=>{ Console.WriteLine("方法1"); }), new Action(()=>{ Console.WriteLine("方法2"); }) }; Parallel.Invoke(actions);
위 내용은 C#의 Thread, Task, Async/Await, IAsyncResult 그래픽 코드에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!