> 백엔드 개발 > C#.Net 튜토리얼 > C#의 Thread, Task, Async/Await, IAsyncResult 그래픽 코드에 대한 자세한 설명

C#의 Thread, Task, Async/Await, IAsyncResult 그래픽 코드에 대한 자세한 설명

黄舟
풀어 주다: 2017-03-29 11:28:10
원래의
3342명이 탐색했습니다.

이 글에서는 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.Task

Task는 .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 TaskTask는 반환 값이 있는 Task이고 TResult는 반환 값 유형입니다.

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/await

async/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,而且只能出现在已经用async关键字修饰的异步方法中。通常情况下,async/await成对出现才有意义,

看看运行结果:

可以看出来,main函数调用GetStrLengthAsync方法后,在await之前,都是同步执行的,直到遇到await关键字,main函数才返回继续执行。

那么是否是在遇到await关键字的时候程序自动开启了一个后台线程去执行GetString方法呢?

现在把GetString方法中的那行注释加上,运行的结果是:

大家可以看到,在遇到await关键字后,没有继续执行GetStrLengthAsync方法后面的操作,也没有马上反回到main函数中,而是执行了GetString的第一行,以此可以判断await这里并没有开启新的线程去执行GetString方法,而是以同步的方式让GetString方法执行,等到执行到GetString方法中的Task.Run()的时候才由Task开启了后台线程!

那么await的作用是什么呢?

可以从字面上理解,上面提到task.wait可以让主线程等待后台线程执行完毕,await和wait类似,同样是等待,等待Task.Run()开始的后台线程执行完毕,不同的是await不会阻塞主线程,只会让GetStrLengthAsync方法暂停执行。

那么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[]数组里面的方法:

Action[] actions = new Action[] { 
 new Action(()=>{
  Console.WriteLine("方法1");
 }),
 new Action(()=>{
  Console.WriteLine("方法2");
 })
};
Parallel.Invoke(actions);
로그인 후 복사

위 내용은 C#의 Thread, Task, Async/Await, IAsyncResult 그래픽 코드에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿