中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在C#項目中實現一個多線程

發布時間:2021-01-26 15:44:17 來源:億速云 閱讀:213 作者:Leah 欄目:編程語言

這期內容當中小編將會給大家帶來有關如何在C#項目中實現一個多線程,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

首先是生成 隨機數,使用 System.Random 類來生成偽隨機數(這個其實性能和效率賊低,后面再敘述)

private int GenerateInt32Num()
{
 var num = random.Next(0, TOTAL_NUM);
 return num;
}

然后是插入到 List<Int32> 中的代碼,判斷是否 已經達到了 我們需要的 List 長度,如果已滿足,則退出程序。

private void AddToList(int num)
{
 if (numList.Count == ENDNUM)
 {
 return;
 }

 numList.Add(num);
}

如果是個 單線程的,按照上面那樣 while(true) 然后一直插入即可,可這個是個 多線程,那么需要如何處理呢?

我思考了一下,想到了之前在 CLR 中學到的 可以用 CancellationTokenSource 中的 Cancel 來通知 Task 來取消操作。所以現在的邏輯是,用線程池來實現多線程。然后傳入 CancellationTokenSource.Token 來取消任務。

最后用 Task.WhanAny() 來獲取到第一個到達此 Task 的 ID。

首先是建立 Task[] 的數組

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);
 }
}

看起來是沒有什么問題的,運行了一下。得到了如下結果,

如何在C#項目中實現一個多線程

這應該是昨晚運行時得到的數據,當時也沒有多想,就貼了上去,回答了那位提問同學的問題。但是心里有一個疑惑,為什么會同時由 兩個 Thread 同時達到了該目標呢?

發現問題

今天早上到公司時,我又打開了這個 代碼,發現確實有點不對勁,于是就和我邊上 做 Go 語言開發的同學,問了問他,哪里出現了問題,他和我說:“你加了讀寫鎖了嗎?” 你這里有數據臟讀寫。心里面有了點眉目。

按照他說的,修改了一下AddToList 里面的邏輯,這時候,確實解決了上面的問題,

private void AddToList(int num)
{
 rwls.EnterReadLock();
 if (numList.Count == ENDNUM)
 return;
 rwls.ExitReadLock();

 rwls.EnterWriteLock();
 numList.Add(num);
 rwls.ExitWriteLock();
}

得到的結果如下:

如何在C#項目中實現一個多線程

完整的代碼如下所示:

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);
 }
 }
}

這時候,那位 Go 語言的同學和我說,我們試試 1000w 的數據插入,看看需要多少時間?于是我讓他用 Go 語言實現了一下上面的邏輯,1000w數據用了 三分鐘,我讓他看看總共生成了多少隨機數,他查看了一下生成了 1億4千多萬的數據。

最開始我用上面的代碼來測,發現我插入 1000w 的數據,CPU 到100% 而且花了挺長時間,程序根本沒反應,查看了一下我判斷重復的語句numList.Contains()

底層實現的代碼為:

[__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;
 }

可想而知,如果數據量很大的話,這個循環不就 及其緩慢嗎?

我于是請教了那位 GO 的同學,判斷重復的邏輯用什么來實現的,他和我說了一個位圖 bitmap 的概念,

我用其重寫了一下判斷重復的邏輯,代碼如下:

int[] bitmap = new int[MAX_SIZE];

var index = num % TOTAL_NUM;
bitMap[index] = 1;

return bitMap[num] == 1;

在添加到 List 的時候,順便插入到 bitmap 中,判斷重復只需要根據當前元素的位置是否 等于 1 即可,

我修改代碼后,跑了一下 1000w 的數據用來 3000+ ms。

這時候,引起了他的極度懷疑,一向以高性能并發 著稱的 Go 速度竟然這么慢嗎?他一度懷疑我的邏輯有問題。

下午結束了一個階段的工作后,我又拾起了我上午寫的代碼,果不其然,發現了邏輯錯誤:

如下:

var insertNum = GenerateInt32Num();
if (numList.Contains(insertNum))
{
 insertNum = GenerateInt32Num();
}

生成隨機數這里,這里有個大問題,就是其實只判斷了一次,導致速度那么快,正確的寫法應該是

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#項目中實現一個多線程了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

泸定县| 绍兴市| 忻城县| 涞水县| 济宁市| 崇明县| 葵青区| 含山县| 宜川县| 赣州市| 安塞县| 钟祥市| 奇台县| 韶关市| 比如县| 西乌珠穆沁旗| 山西省| 治县。| 乌什县| 勃利县| 改则县| 乌兰察布市| 华池县| 仲巴县| 镇安县| 晴隆县| 乐至县| 丰城市| 朝阳市| 六枝特区| 大英县| 龙井市| 彰武县| 旬阳县| 含山县| 高台县| 郯城县| 仲巴县| 深泽县| 柯坪县| 浠水县|