網(wǎng)站首頁 編程語言 正文
本文主要來自一道面試題,由于之前對(duì)AutoResetEvent的概念比較模糊(即使已經(jīng)使用過了)。面試題題目很簡潔:兩個(gè)線程交替打印0~100的奇偶數(shù)。你可以先動(dòng)手試試,我主要是嘗試在一個(gè)方法里面完成這個(gè)任務(wù)。
注: Suspend,Resume來控制線程已經(jīng)在.net framework2.0被淘汰了,原因就是掛起之后,但因?yàn)楫惓6鴽]有及時(shí)恢復(fù),如果占用資源會(huì)導(dǎo)致死鎖。
- AutoResetEvent對(duì)象用來進(jìn)行線程同步操作,AutoResetEvent類繼承waitHandle類。waitOne()方法就繼承來自waitHandle類。
- AutoResetEvent對(duì)象有終止和非終止兩種狀態(tài),終止?fàn)顟B(tài)是線程繼續(xù)執(zhí)行,非終止?fàn)顟B(tài)使線程阻塞,可以調(diào)用set和reset方法使對(duì)象進(jìn)入終止和非終止?fàn)顟B(tài)。-》可以簡單理解如果AutoResetEvent對(duì)象是終止?fàn)顟B(tài),就像不管別人了,任你撒野去(waitOne()得到的都是撒野信號(hào))
- AutoResetEvent顧名思義,其對(duì)象在調(diào)用一次set之后會(huì)自動(dòng)調(diào)用一次reset,進(jìn)入非終止?fàn)顟B(tài)使調(diào)用了等待方法的線程進(jìn)入阻塞狀態(tài)。-》可以簡單理解如果AutoResetEvent對(duì)象是非終止?fàn)顟B(tài),就開始管理起別人來了,此時(shí)waitOne()得到的信號(hào)都是呆在原地不動(dòng)信號(hào)。
- waitHandle對(duì)象的waitone可以使當(dāng)前線程進(jìn)入阻塞狀態(tài),等待一個(gè)信號(hào)。直到當(dāng)前 waitHandle對(duì)象收到信號(hào),才會(huì)繼續(xù)執(zhí)行。
- set可以發(fā)送一個(gè)信號(hào),允許一個(gè)調(diào)用waitone而等待線程繼續(xù)執(zhí)行。 ManulResetEvent的set方法可以允許多個(gè)。但是要手動(dòng)關(guān)閉,即調(diào)用reset();
- reset可以使因?yàn)檎{(diào)用waitone() 而等待線程都進(jìn)入阻塞狀態(tài)。
AutoResetEvent主要方法及實(shí)踐
- AutoResetEvent(bool initialState):構(gòu)造函數(shù),用一個(gè)指示是否將初始狀態(tài)設(shè)置為終止的布爾值初始化該類的新實(shí)例。 false:無信號(hào),子線程的WaitOne方法不會(huì)被自動(dòng)調(diào)用 true:有信號(hào),子線程的WaitOne方法會(huì)被自動(dòng)調(diào)用
- Reset ():將事件狀態(tài)設(shè)置為非終止?fàn)顟B(tài),導(dǎo)致線程阻止;如果該操作成功,則返回true;否則,返回false。
- Set ():將事件狀態(tài)設(shè)置為終止?fàn)顟B(tài),允許一個(gè)或多個(gè)等待線程繼續(xù);如果該操作成功,則返回true;否則,返回false。
- WaitOne(): 阻止當(dāng)前線程,直到收到信號(hào)。
- WaitOne(TimeSpan, Boolean) :阻止當(dāng)前線程,直到當(dāng)前實(shí)例收到信號(hào),使用 TimeSpan 度量時(shí)間間隔并指定是否在等待之前退出同步域。
有了上面的解釋,開始展示代碼(經(jīng)過多次優(yōu)化)
//若要將初始狀態(tài)設(shè)置為終止,則為 true;若要將初始狀態(tài)設(shè)置為非終止,則為 false
static AutoResetEvent oddResetEvent = new AutoResetEvent(false);
static AutoResetEvent evenResetEvent = new AutoResetEvent(false);
static int i = 0;
static void Main(string[] args)
{
//ThreadStart是個(gè)委托
Thread thread1 = new Thread(new ThreadStart(show));
thread1.Name = "偶數(shù)線程";
Thread thread2 = new Thread(new ThreadStart(show));
thread2.Name = "奇數(shù)線程";
thread1.Start();
Thread.Sleep(2); //保證偶數(shù)線程先運(yùn)行。
thread2.Start();
Console.Read();
}
public static void show()
{
while (i <= 100)
{
int num = i % 2;
if (num == 0)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, "evenResetEvent");
if(i!=1) evenResetEvent.Set();
oddResetEvent.WaitOne(); //當(dāng)前線程阻塞
}
else
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, "oddResetEvent");
//如果此時(shí)AutoResetEvent 為非終止?fàn)顟B(tài),則線程會(huì)被阻止,并等待當(dāng)前控制資源的線程通過調(diào)用 Set 來通知資源可用。否則不會(huì)被阻止
oddResetEvent.Set();
evenResetEvent.WaitOne();
}
}
}
結(jié)果如下圖所示:
注意點(diǎn):
不要有一點(diǎn)點(diǎn)點(diǎn)點(diǎn)多余的evenResetEvent.Set(),他會(huì)讓后續(xù)的 evenResetEvent.WaitOne();失效.
第二種方法Semaphore
此外,我們利用信號(hào)量也可以實(shí)現(xiàn),信號(hào)量是一種內(nèi)核模式鎖,對(duì)性能要求比較高,特殊情況下才考慮使用,而且要避免在內(nèi)核模式和用戶模式下頻繁相互切換線程。代碼如下:
private static readonly int MaxSize = 1;
private static int i = 0;
static Semaphore oddSemaphore = new Semaphore(0, MaxSize);
static Semaphore evenSemaphore = new Semaphore(0, MaxSize);
static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//ThreadStart是個(gè)委托
Thread thread1 = new Thread(new ThreadStart(show));
thread1.Name = "偶數(shù)線程";
Thread thread2 = new Thread(new ThreadStart(show));
thread2.Name = "奇數(shù)線程";
thread1.Start();
thread2.Start();
thread1.Join();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
}
private static void show()
{
if(i==1) evenSemaphore.WaitOne();
while (i <= 100)
{
int num = i % 2;
if (num == 0)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
evenSemaphore.Release();
oddSemaphore.WaitOne(); //當(dāng)前線程阻塞
}
else
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
//釋放一個(gè)偶數(shù)信號(hào)空位出來;
oddSemaphore.Release();
evenSemaphore.WaitOne(); //當(dāng)前線程阻塞
//此時(shí)已經(jīng)消耗了一個(gè)奇數(shù)信號(hào)空位
}
}
}
第三種方法,約定每個(gè)線程只干自己的事
這種方法利用線程池本身就是隊(duì)列的方式,即先進(jìn)先出。測試之后發(fā)現(xiàn)性能有下降,但是還是貼出來供參考。
static int threadCount = 2;
static int count = 0;
static object cursorLock = new object();
static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
Task[] arr = new Task[2];
for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)
{
//這兩種方法都可以
arr[threadIndex] = Task.Factory.StartNew(PrintNum, threadIndex);
}
Task.WaitAll(arr);
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
}
private static void PrintNum(object num)
{
bool isOk = false;
while (!isOk)
{
lock (cursorLock)
{
int index = count % 2;
if (count>100)
{
isOk = true;
}
else if (index == (int)num)
{
if (index == 0) Console.WriteLine("{0}:{1} {2} ", "偶數(shù)線程", Thread.CurrentThread.ManagedThreadId, count++);
else Console.WriteLine("{0}:{1} {2} ", "奇數(shù)線程", Thread.CurrentThread.ManagedThreadId, count++);
}
}
}
}
結(jié)果如下:
第四種方法 Mutex
private static int i = 0;
static Mutex mutex = new Mutex();
static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//ThreadStart是個(gè)委托
Thread thread1 = new Thread(new ParameterizedThreadStart(show));
thread1.Name = "偶數(shù)線程";
Thread thread2 = new Thread(new ParameterizedThreadStart(show));
thread2.Name = "奇數(shù)線程";
thread1.Start(0);
thread2.Start(1);
thread2.Join();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
}
/// <summary>
/// Mutex的釋放與鎖定 都只能在同一個(gè)線程中執(zhí)行
/// </summary>
private static void show(object index)
{
while (i <= 100)
{
mutex.WaitOne();
int num = i % 2;
if (num == (int)index&&i<=100)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
}
mutex.ReleaseMutex();
}
}
原文鏈接:https://www.cnblogs.com/zhan520g/p/11388591.html
相關(guān)推薦
- 2022-11-22 GraphQL在react中的應(yīng)用示例詳解_React
- 2022-10-24 C++??STL?_?Vector使用及模擬實(shí)現(xiàn)_C 語言
- 2022-08-10 C++學(xué)習(xí)之多態(tài)的使用詳解_C 語言
- 2022-02-27 Redux及React-redux的簡單使用
- 2023-05-15 使用Bash讀取和處理CSV文件的方法_linux shell
- 2022-12-03 C#通過Builder模式造車_C#教程
- 2022-04-20 C語言數(shù)據(jù)結(jié)構(gòu)與算法之時(shí)間空間復(fù)雜度入門_C 語言
- 2022-07-16 ffmpeg中AVFrame\AVPacket與自己的數(shù)據(jù)交互
- 欄目分類
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支