這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)如何在C#項(xiàng)目中實(shí)現(xiàn)一個(gè)多線程,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),安福網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:安福等地區(qū)。安福做網(wǎng)站價(jià)格咨詢:18980820575
首先是生成 隨機(jī)數(shù),使用 System.Random 類來(lái)生成偽隨機(jī)數(shù)(這個(gè)其實(shí)性能和效率賊低,后面再敘述)
private int GenerateInt32Num()
{
var num = random.Next(0, TOTAL_NUM);
return num;
}然后是插入到 List<Int32> 中的代碼,判斷是否 已經(jīng)達(dá)到了 我們需要的 List 長(zhǎng)度,如果已滿足,則退出程序。
private void AddToList(int num)
{
if (numList.Count == ENDNUM)
{
return;
}
numList.Add(num);
}如果是個(gè) 單線程的,按照上面那樣 while(true) 然后一直插入即可,可這個(gè)是個(gè) 多線程,那么需要如何處理呢?
我思考了一下,想到了之前在 CLR 中學(xué)到的 可以用 CancellationTokenSource 中的 Cancel 來(lái)通知 Task 來(lái)取消操作。所以現(xiàn)在的邏輯是,用線程池來(lái)實(shí)現(xiàn)多線程。然后傳入 CancellationTokenSource.Token 來(lái)取消任務(wù)。
最后用 Task.WhanAny() 來(lái)獲取到第一個(gè)到達(dá)此 Task 的 ID。
首先是建立 Task[] 的數(shù)組
internal void DoTheCompeteSecond()
{
Task[] tasks = new Task[10];
for (int i = 0; i < 10; ++i)
{
int num = i;
tasks[i] = Task.Factory.StartNew(() => AddNumToList(num, cts), cts.Token);
}
Task.WaitAny(tasks);
}然后 AddNumToList 方法是這樣定義的,
private void AddNumToList(object state, CancellationTokenSource cts)
{-
Console.WriteLine("This is the {0} thread,Current ThreadId={1}",
state,
Thread.CurrentThread.ManagedThreadId);
while (!cts.Token.IsCancellationRequested)
{
if (GetTheListCount() == ENDNUM)
{
cts.Cancel();
Console.WriteLine("Current Thread Id={0},Current Count={1}",
Thread.CurrentThread.ManagedThreadId,
GetTheListCount());
break;
}
var insertNum = GenerateInt32Num();
if (numList.Contains(insertNum))
{
insertNum = GenerateInt32Num();
}
AddToList(insertNum);
}
}看起來(lái)是沒(méi)有什么問(wèn)題的,運(yùn)行了一下。得到了如下結(jié)果,

這應(yīng)該是昨晚運(yùn)行時(shí)得到的數(shù)據(jù),當(dāng)時(shí)也沒(méi)有多想,就貼了上去,回答了那位提問(wèn)同學(xué)的問(wèn)題。但是心里有一個(gè)疑惑,為什么會(huì)同時(shí)由 兩個(gè) Thread 同時(shí)達(dá)到了該目標(biāo)呢?
發(fā)現(xiàn)問(wèn)題
今天早上到公司時(shí),我又打開(kāi)了這個(gè) 代碼,發(fā)現(xiàn)確實(shí)有點(diǎn)不對(duì)勁,于是就和我邊上 做 Go 語(yǔ)言開(kāi)發(fā)的同學(xué),問(wèn)了問(wèn)他,哪里出現(xiàn)了問(wèn)題,他和我說(shuō):“你加了讀寫鎖了嗎?” 你這里有數(shù)據(jù)臟讀寫。心里面有了點(diǎn)眉目。
按照他說(shuō)的,修改了一下AddToList 里面的邏輯,這時(shí)候,確實(shí)解決了上面的問(wèn)題,
private void AddToList(int num)
{
rwls.EnterReadLock();
if (numList.Count == ENDNUM)
return;
rwls.ExitReadLock();
rwls.EnterWriteLock();
numList.Add(num);
rwls.ExitWriteLock();
}得到的結(jié)果如下:

完整的代碼如下所示:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace CSharpFundamental
{
class MultipleThreadCompete
{
List<int> numList = new List<int>();
Random random = new Random();
CancellationTokenSource cts = new CancellationTokenSource();
private const int ENDNUM = 1000000;
ReaderWriterLockSlim rwls = new ReaderWriterLockSlim();
internal void DoTheCompeteSecond()
{
Stopwatch sw = new Stopwatch();
sw.Start();
Task[] tasks = new Task[100];
for (int i = 0; i < 100; ++i)
{
int num = i;
tasks[i] = Task.Run(() => AddNumToList(num, cts), cts.Token);
}
Task.WaitAny(tasks);
Console.WriteLine("ExecuteTime={0}", sw.ElapsedMilliseconds / 1000);
}
private int GetTheListCount()
{
return numList.Count;
}
private void AddToList(int num)
{
rwls.EnterReadLock();
if (numList.Count == ENDNUM)
return;
rwls.ExitReadLock();
rwls.EnterWriteLock();
numList.Add(num);
rwls.ExitWriteLock();
}
private void AddNumToList(object state, CancellationTokenSource cts)
{
Console.WriteLine("This is the {0} thread,Current ThreadId={1}",
state,
Thread.CurrentThread.ManagedThreadId);
while (!cts.Token.IsCancellationRequested)
{
try
{
rwls.EnterReadLock();
if (numList.Count == ENDNUM)
{
cts.Cancel();
Console.WriteLine("Current Thread Id={0},Current Count={1}",
Thread.CurrentThread.ManagedThreadId,
GetTheListCount());
break;
}
}
finally
{
rwls.ExitReadLock();
}
var insertNum = GenerateInt32Num();
if (numList.Contains(insertNum))
{
insertNum = GenerateInt32Num();
}
AddToList(insertNum);
}
}
private int GenerateInt32Num()
{
return random.Next(1, ENDNUM);
}
}
}這時(shí)候,那位 Go 語(yǔ)言的同學(xué)和我說(shuō),我們?cè)囋?1000w 的數(shù)據(jù)插入,看看需要多少時(shí)間?于是我讓他用 Go 語(yǔ)言實(shí)現(xiàn)了一下上面的邏輯,1000w數(shù)據(jù)用了 三分鐘,我讓他看看總共生成了多少隨機(jī)數(shù),他查看了一下生成了 1億4千多萬(wàn)的數(shù)據(jù)。
最開(kāi)始我用上面的代碼來(lái)測(cè),發(fā)現(xiàn)我插入 1000w 的數(shù)據(jù),CPU 到100% 而且花了挺長(zhǎng)時(shí)間,程序根本沒(méi)反應(yīng),查看了一下我判斷重復(fù)的語(yǔ)句numList.Contains()
底層實(shí)現(xiàn)的代碼為:
[__DynamicallyInvokable]
public bool Contains(T item)
{
if ((object) item == null)
{
for (int index = 0; index < this._size; ++index)
{
if ((object) this._items[index] == null)
return true;
}
return false;
}
EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
for (int index = 0; index < this._size; ++index)
{
if (equalityComparer.Equals(this._items[index], item))
return true;
}
return false;
}可想而知,如果數(shù)據(jù)量很大的話,這個(gè)循環(huán)不就 及其緩慢嗎?
我于是請(qǐng)教了那位 GO 的同學(xué),判斷重復(fù)的邏輯用什么來(lái)實(shí)現(xiàn)的,他和我說(shuō)了一個(gè)位圖 bitmap 的概念,
我用其重寫了一下判斷重復(fù)的邏輯,代碼如下:
int[] bitmap = new int[MAX_SIZE]; var index = num % TOTAL_NUM; bitMap[index] = 1; return bitMap[num] == 1;
在添加到 List 的時(shí)候,順便插入到 bitmap 中,判斷重復(fù)只需要根據(jù)當(dāng)前元素的位置是否 等于 1 即可,
我修改代碼后,跑了一下 1000w 的數(shù)據(jù)用來(lái) 3000+ ms。
這時(shí)候,引起了他的極度懷疑,一向以高性能并發(fā) 著稱的 Go 速度竟然這么慢嗎?他一度懷疑我的邏輯有問(wèn)題。
下午結(jié)束了一個(gè)階段的工作后,我又拾起了我上午寫的代碼,果不其然,發(fā)現(xiàn)了邏輯錯(cuò)誤:
如下:
var insertNum = GenerateInt32Num();
if (numList.Contains(insertNum))
{
insertNum = GenerateInt32Num();
}生成隨機(jī)數(shù)這里,這里有個(gè)大問(wèn)題,就是其實(shí)只判斷了一次,導(dǎo)致速度那么快,正確的寫法應(yīng)該是
while (ContainsNum(currentNum))
{
currentNum = GenerateInt32Num();
}
private int GenerateInt32Num()
{
var num = random.Next(0, TOTAL_NUM);
//Console.WriteLine(num);
return num;
}最后的代碼如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace CSharpFundamental
{
class MultipleThreadCompete
{
List<int> numList = new List<int>();
Random random = new Random();
CancellationTokenSource cts = new CancellationTokenSource();
private const int TOTAL_NUM = 1000000;
private const int CURRENT_THREAD_COUNT = 35;
ReaderWriterLockSlim rwls = new ReaderWriterLockSlim();
int[] bitMap = new int[TOTAL_NUM];
internal void DoTheCompete()
{
//ThreadPool.SetMinThreads(CURRENT_THREAD_COUNT, CURRENT_THREAD_COUNT);
Stopwatch sw = new Stopwatch();
sw.Start();
Task[] tasks = new Task[CURRENT_THREAD_COUNT];
for (int i = 0; i < CURRENT_THREAD_COUNT; ++i)
{
int num = i;
tasks[i] = Task.Run(() => ExecuteTheTask(num, cts), cts.Token);
}
Task.WaitAny(tasks);
Console.WriteLine("ExecuteTime={0}", sw.ElapsedMilliseconds);
}
private int GetTheListCount()
{
return numList.Count;
}
private void AddToList(int num)
{
if (numList.Count == TOTAL_NUM)
return;
numList.Add(num);
var index = num % TOTAL_NUM;
bitMap[index] = 1;
}
private void ExecuteTheTask(object state, CancellationTokenSource cts)
{
Console.WriteLine("This is the {0} thread,Current ThreadId={1}",
state,
Thread.CurrentThread.ManagedThreadId);
while (!cts.Token.IsCancellationRequested)
{
try
{
rwls.EnterReadLock();
if (numList.Count == TOTAL_NUM)
{
cts.Cancel();
Console.WriteLine("Current Thread Id={0},Current Count={1}",
Thread.CurrentThread.ManagedThreadId,
GetTheListCount());
break;
}
}
finally
{
rwls.ExitReadLock();
}
var currentNum = GenerateInt32Num();
while (ContainsNum(currentNum))
{
currentNum = GenerateInt32Num();
}
rwls.EnterWriteLock();
AddToList(currentNum);
rwls.ExitWriteLock();
}
}
private int GenerateInt32Num()
{
var num = random.Next(0, TOTAL_NUM);
//Console.WriteLine(num);
return num;
}
private bool ContainsNum(int num)
{
rwls.EnterReadLock();
var contains = bitMap[num] == 1;
rwls.ExitReadLock();
return contains;
}
}
}上述就是小編為大家分享的如何在C#項(xiàng)目中實(shí)現(xiàn)一個(gè)多線程了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前標(biāo)題:如何在C#項(xiàng)目中實(shí)現(xiàn)一個(gè)多線程
網(wǎng)站URL:http://chinadenli.net/article24/gdseje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、微信公眾號(hào)、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站改版、網(wǎng)站制作、虛擬主機(jī)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)