欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c# 進(jìn)程之間的線程同步

 更新時(shí)間:2020年10月30日 08:34:53   作者:一只獨(dú)行的猿  
這篇文章主要介紹了c# 進(jìn)程之間的線程同步,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下

  Mutex類、Event類、SemaphoreSlim類和ReaderWriterLockSlim類等提供了多個(gè)進(jìn)程之間的線程同步。

 1、WaitHandle 基類

  WaitHandle抽象類,用于等待一個(gè)信號(hào)的設(shè)置??梢愿鶕?jù)其派生類的不同,等待不同的信號(hào)。異步委托的BeginInvoke()方法返回一個(gè)實(shí)現(xiàn)了IAsycResult接口的對(duì)象。使用IAsycResult接口可以用AsycWaitHandle屬性訪問WaitHandle基類。在調(diào)用WaitOne()方法時(shí),線程會(huì)等待接收一個(gè)和等待句柄相關(guān)的信號(hào):

static void Main(string[] args)
{
  Func<int> func = new Func<int>(
    () =>
    {
      Thread.Sleep(1500);
      return 1;
    });
  IAsyncResult ar = func.BeginInvoke(null, null);
  int count = 0;
  while (true)
  {
    Interlocked.Increment(ref count);
    Console.WriteLine("第{0}周期循環(huán)等待結(jié)果。", count);
    if (ar.AsyncWaitHandle.WaitOne(100, false))
    {
      Console.WriteLine("獲得返回結(jié)果。");
      break;
    }
  }
  int result = func.EndInvoke(ar);
  Console.WriteLine("結(jié)果為:{0}", result);
}

  使用WaitHandle基類可以等待一個(gè)信號(hào)的出現(xiàn)(WaitHandle()方法)、等待多個(gè)對(duì)象都必須發(fā)出信號(hào)(WaitAll()方法)、等待多個(gè)對(duì)象中任一一個(gè)發(fā)出信號(hào)(WaitAny()方法)。其中WaitAll()方法和WaitAny()方法時(shí)WaitHandle類的靜態(tài)方法,接收一個(gè)WaitHandle參數(shù)數(shù)組。

  WaitHandle基類的SafeWaitHandle屬性,其中可以將一個(gè)本機(jī)句柄賦予一個(gè)系統(tǒng)資源,等待該句柄,如I/O操作,或者自定義的句柄。

2、Mutex 類

  Mutex類繼承自WaitHandle類,提供跨多個(gè)進(jìn)程同步訪問的一個(gè)類。類似于Monitor類,只能有一個(gè)線程擁有鎖定。在Mutex類的構(gòu)造函數(shù)各參數(shù)含義:

  • initiallyOwned: 如果為 true,則給予調(diào)用線程已命名的系統(tǒng)互斥體的初始所屬權(quán)(如果已命名的系統(tǒng)互斥體是通過此調(diào)用創(chuàng)建的);否則為 false。
  • name:系統(tǒng)互斥體的名稱。 如果值為 null,則 System.Threading.Mutex 是未命名的。
  • createdNew: 在此方法返回時(shí),如果創(chuàng)建了局部互斥體(即,如果 name 為 null 或空字符串)或指定的命名系統(tǒng)互斥體,則包含布爾值 true;如果指定的命名系統(tǒng)互斥體已存在,則為false。 該參數(shù)未經(jīng)初始化即被傳遞。
  • mutexSecurity: 一個(gè) System.Security.AccessControl.MutexSecurity 對(duì)象,表示應(yīng)用于已命名的系統(tǒng)互斥體的訪問控制安全性。

  互斥也可以在另一個(gè)進(jìn)程中定義,操作系統(tǒng)能夠識(shí)別有名稱的互斥,它由進(jìn)程之間共享。如果沒有指定互斥的名稱,則不在不同的進(jìn)程之間共享。該方法可以檢測(cè)程序是否已運(yùn)行,可以禁止程序啟動(dòng)兩次。

static void Main(string[] args)
{
  // ThreadingTimer();
  // TimersTimer();
  bool isCreateNew = false;
  Mutex mutex = new Mutex(false, "MyApp", out isCreateNew);//查詢是否已有互斥“MyApp”存在
  if(isCreateNew==false)
  {
    //已存在互斥
  }
}

  要打開已有互斥,可以使用Mutex.OpenExisting()方法,不需要構(gòu)造函數(shù)創(chuàng)建互斥時(shí)需要的相同.Net權(quán)限??梢允褂肳aitOne()方法獲得互斥的鎖定,成為該互斥的擁有著。調(diào)用ReleaseMutex()方法釋放互斥:

if(mutex.WaitOne())//設(shè)置互斥鎖定
{
  try
  {
    //執(zhí)行代碼
  }
  finally {
    mutex.ReleaseMutex();//釋放互斥
  }
}
else
{
  //出現(xiàn)問題
}

3、Semaphore 類

  信號(hào)量是一種計(jì)數(shù)的互斥鎖定,可以同時(shí)由多個(gè)線程使用。信號(hào)量可定義允許同時(shí)訪問受旗語鎖定保護(hù)的資源的線程個(gè)數(shù)。Semaphore和SemaphoreSlim兩個(gè)類具有信號(hào)量功能。Semaphore類可以指定名稱,讓其在系統(tǒng)資源范圍內(nèi)查找到,允許在不同的進(jìn)程之間同步。Semaphore類是對(duì)較短等待時(shí)間進(jìn)行優(yōu)化了的輕型版本。

static void Main()
{
  int taskCount = 6;
  int semaphoreCount = 3;
  Semaphore semaphore = new Semaphore(semaphoreCount, semaphoreCount, "Test");//創(chuàng)建計(jì)數(shù)為3的信號(hào)量
  /* 第一個(gè)參數(shù)為初始釋放的鎖定數(shù),第二個(gè)參數(shù)為可鎖定的總數(shù)。如果第一個(gè)參數(shù)小于第二個(gè)參數(shù),其差值就是已分配線程的計(jì)量數(shù)。
   * 第三個(gè)參數(shù)為信號(hào)指定的名稱,能讓它在不同的進(jìn)程之間共享。
   */
  var tasks = new Task[taskCount];

  for (int i = 0; i < taskCount; i++)
  {
    tasks[i] = Task.Run(() => TaskMain(semaphore));//創(chuàng)建6個(gè)任務(wù)
  }

  Task.WaitAll(tasks);

  Console.WriteLine("All tasks finished");
}

//鎖定信號(hào)的任務(wù)
static void TaskMain(Semaphore semaphore)
{
  bool isCompleted = false;
  while (!isCompleted)//循環(huán)等待被釋放的信號(hào)量
  {
    if (semaphore.WaitOne(600))//最長(zhǎng)等待600ms
    {
      try
      {
        Console.WriteLine("Task {0} locks the semaphore", Task.CurrentId);
        Thread.Sleep(2000);//2s后釋放信號(hào)
      }
      finally
      {
        Console.WriteLine("Task {0} releases the semaphore", Task.CurrentId);
        semaphore.Release();//釋放信號(hào)量
        isCompleted = true;
      }
    }
    else
    {
      //超過規(guī)定的等待時(shí)間,寫入一條超時(shí)等待的信息
      Console.WriteLine("Timeout for task {0}; wait again", Task.CurrentId);
    }
  }
}

  以上方法中,信號(hào)量計(jì)數(shù)為3,因此最多只有三個(gè)任務(wù)可獲得鎖定,第4個(gè)及以后的任務(wù)必須等待。在解除鎖定時(shí),任何情況下一定要解除資源的鎖定。

4、Events 類

  事件也是一個(gè)系統(tǒng)范圍內(nèi)資源同步的方法。主要由以下幾個(gè)類提供:ManualResetEvent、AutoResetEvent、ManualResetEventSlim、和CountdownEvent類。

  ManualResetEventSlim類中,調(diào)用Set()方法可以發(fā)出信號(hào);調(diào)用Reset()方法可以使重置為無信號(hào)狀態(tài)。如果多個(gè)線程在等待向一個(gè)事件發(fā)出信號(hào),并調(diào)用Set()方法,就釋放所有等待線程。如果一個(gè)線程剛剛調(diào)用了WiatOne()方法,但事件已發(fā)出信號(hào),等待的線程就可以繼續(xù)等待。

  AutoResetEvent類中,同樣可以通過Set()方法發(fā)出信號(hào)、Reset()方法重置信號(hào),但是該類是自動(dòng)重置信號(hào)。如果一個(gè)線程在等待自動(dòng)重置的事件發(fā)信號(hào),當(dāng)?shù)谝粋€(gè)線程的等待狀態(tài)結(jié)束時(shí),該事件會(huì)自動(dòng)變?yōu)椴话l(fā)信號(hào)的狀態(tài)。即:如果多個(gè)線程在等待向事件發(fā)信號(hào),只有一個(gè)線程結(jié)束其等待狀態(tài),它不是等待事件最長(zhǎng)的線程,而是優(yōu)先級(jí)最高的線程。

//計(jì)算數(shù)據(jù)的類,使用ManualResetEventSlim類的示例
public class Calculator
{
  private ManualResetEventSlim mEvent;
  public int Result { get; private set; }
  public Calculator(ManualResetEventSlim ev)
  {
    this.mEvent = ev;
  }
  public void Calculation(int x, int y)
  {
    Console.WriteLine("Task {0} starts calculation", Task.CurrentId);
    Thread.Sleep(new Random().Next(3000));//隨機(jī)等待事件
    Result = x + y;//計(jì)算結(jié)果

    Console.WriteLine("Task {0} is ready", Task.CurrentId);
    mEvent.Set();//發(fā)出完成信號(hào)
  }
}
//外部調(diào)用的示例:
static void Main()
{
  const int taskCount = 10;

  ManualResetEventSlim[] mEvents = new ManualResetEventSlim[taskCount];

  WaitHandle[] waitHandles = new WaitHandle[taskCount];
  var calcs = new Calculator[taskCount];

  for (int i = 0; i < taskCount; i++)
  {
    int i1 = i;//目的是使后面要執(zhí)行的Task不必等待執(zhí)行完后才釋放i,讓for繼續(xù)
    mEvents[i] = new ManualResetEventSlim(false);//對(duì)應(yīng)任務(wù)的事件對(duì)象發(fā)出信號(hào)
    waitHandles[i] = mEvents[i].WaitHandle;//ManualResetEvent類派生自WaitHandle類,但ManualResetEventSlim并不是,因此需要保存其WaitHandle對(duì)象
    calcs[i] = new Calculator(mEvents[i]);

    Task.Run(() => calcs[i1].Calculation(i1 + 1, i1 + 3));
  }

  for (int i = 0; i < taskCount; i++)
  {
    int index = WaitHandle.WaitAny(waitHandles);//等待任何一個(gè)發(fā)出信號(hào),并返回發(fā)出信號(hào)的索引
    if (index == WaitHandle.WaitTimeout)
    {
      Console.WriteLine("Timeout!!");
    }
    else
    {
      mEvents[index].Reset();//重新設(shè)置為無信號(hào)狀態(tài)
      Console.WriteLine("finished task for {0}, result: {1}", index, calcs[index].Result);
    }
  }
}

  CountdownEvent類適用于:需要把一個(gè)工作任務(wù)分配到多個(gè)任務(wù)中,然后在各個(gè)任務(wù)結(jié)束后合并結(jié)果(不需要為每個(gè)任務(wù)單獨(dú)創(chuàng)建事件對(duì)象)。每個(gè)任務(wù)不需要同步。CountdownEvent類為所有設(shè)置了事件的任務(wù)定義了一個(gè)初始數(shù)字,達(dá)到該計(jì)數(shù)后,就發(fā)出信號(hào)。

//修改計(jì)算類
public class Calculator
{
  private CountdownEvent cEvent;
  public int Result { get; private set; }
  public Calculator(CountdownEvent ev)
  {
    this.cEvent = ev;
  }
  public void Calculation(int x, int y)
  {
    Console.WriteLine("Task {0} starts calculation", Task.CurrentId);
    Thread.Sleep(new Random().Next(3000));//隨機(jī)等待事件
    Result = x + y;//計(jì)算結(jié)果
    // signal the event—completed!
    Console.WriteLine("Task {0} is ready", Task.CurrentId);
    cEvent.Signal();//發(fā)出完成信號(hào)
  }
}
//修改方法調(diào)用
static void Main()
{
  const int taskCount = 10;
  CountdownEvent cEvent = new CountdownEvent(taskCount);

  WaitHandle[] waitHandles = new WaitHandle[taskCount];
  var calcs = new Calculator[taskCount];

  for (int i = 0; i < taskCount; i++)
  {
    int i1 = i;//目的是使后面要執(zhí)行的Task不必等待執(zhí)行后才釋放i,讓for可以繼續(xù)
    calcs[i] = new Calculator(cEvent);//為每個(gè)任務(wù)都賦該事件通知類
    Task.Run(() => calcs[i1].Calculation(i1 + 1, i1 + 3));

  }

  cEvent.Wait();//等待一個(gè)事件的信號(hào)
  Console.WriteLine("all finished");
  for (int i = 0; i < taskCount; i++)
  {
    Console.WriteLine("task for {0}, result: {1}", i, calcs[i].Result);
  }
}

 5、Barrier 類

  Barrier類適用于:工作有多個(gè)任務(wù)分支,并且在所有任務(wù)執(zhí)行完后需要合并的工作情況。與CountdownEvent不同于,該類用于需要同步的參與者。在激活一個(gè)任務(wù)后,可以動(dòng)態(tài)的添加其他參與者。在主參與者繼續(xù)之前,可以等待所有其他參與者完成工作。

static void Main()
{
  const int numberTasks = 2;
  const int partitionSize = 1000000;
  var data = new List<string>(FillData(partitionSize * numberTasks));
  var barrier = new Barrier(numberTasks + 1);//定義三個(gè)參與者:一個(gè)主參與者(分配任務(wù)者),兩個(gè)子參與者(被分配任務(wù)者)
  var tasks = new Task<int[]>[numberTasks];//兩個(gè)子參與者
  for (int i = 0; i < numberTasks; i++)
  {
    int jobNumber = i;
    tasks[i] = Task.Run(() => CalculationInTask(jobNumber, partitionSize, barrier, data));//啟動(dòng)計(jì)算任務(wù):可以分開寫,以執(zhí)行多個(gè)不同的任務(wù)。
  }
  barrier.SignalAndWait();// 主參與者以完成,等待子參與者全部完成。
  //合并兩個(gè)結(jié)果(LINQ)
  IEnumerable<int> resultCollection = tasks[0].Result.Zip(tasks[1].Result, (c1, c2) => { return c1 + c2; }).ToList();//立即求和

  char ch = 'a';
  int sum = 0;
  foreach (var x in resultCollection)
  {
    Console.WriteLine("{0}, count: {1}", ch++, x);//輸出結(jié)果
    sum += x;
  }
  Console.WriteLine("main finished {0}", sum);//統(tǒng)計(jì)過的字符串?dāng)?shù)量
  Console.WriteLine("remaining {0}, phase {1}", barrier.ParticipantsRemaining, barrier.CurrentPhaseNumber);//當(dāng)前參與者信息
}

static int[] CalculationInTask(int jobNumber, int partitionSize, Barrier barrier, IList<string> coll)
{
  var data = new List<string>(coll);
  int start = jobNumber * partitionSize;//計(jì)算其實(shí)下標(biāo)
  int end = start + partitionSize;//計(jì)算結(jié)束的位置
  Console.WriteLine("Task {0}: partition from {1} to {2}", Task.CurrentId, start, end);
  int[] charCount = new int[26];
  for (int j = start; j < end; j++)
  {
    char c = data[j][0];//獲取當(dāng)前字符串的第一個(gè)字符
    charCount[c - 97]++;//對(duì)應(yīng)字符的數(shù)量+1;
  }
  Console.WriteLine("Calculation completed from task {0}. {1} times a, {2} times z", Task.CurrentId, charCount[0], charCount[25]);//告知計(jì)算完成
  barrier.RemoveParticipant();//告知,減少一個(gè)參數(shù)者
  Console.WriteLine("Task {0} removed from barrier, remaining participants {1}", Task.CurrentId, barrier.ParticipantsRemaining);
  return charCount;//返回統(tǒng)計(jì)的結(jié)果
}

//用于填充一個(gè)字符串鏈表
public static IEnumerable<string> FillData(int size)
{
  var data = new List<string>(size);
  var r = new Random();
  for (int i = 0; i < size; i++)
  {
    data.Add(GetString(r));//獲得一個(gè)隨機(jī)的字符串
  }
  return data;
}
private static string GetString(Random r)
{
  var sb = new StringBuilder(6);
  for (int i = 0; i < 6; i++)
  {
    sb.Append((char)(r.Next(26) + 97));//創(chuàng)建一個(gè)6個(gè)字符的隨機(jī)字符串
  }
  return sb.ToString();
}

6、ReaderWriterLockSlim 類

  該類是使鎖定機(jī)制允許鎖定多個(gè)讀取器(而不是一個(gè)寫入器)訪問某個(gè)資源:如果沒有寫入器鎖定資源,那么允許多個(gè)讀取器訪問資源,但只能有一個(gè)寫入器鎖定該資源。

  由它的屬性可以讀取是否處于堵塞或不堵塞的鎖定,如EnterReadLock()和TryEnterReadLock()方法。也可以獲得其是否處于寫入鎖定或非鎖定狀態(tài),如EnterWriteLock()和TryEnterWriteLock()方法。如果任務(wù)需要先讀取資源,之后寫入資源,可以使用EnterUpgradeableReadLock()或TryEnterUpgradeableReadLock()方法獲取可升級(jí)的讀取鎖定。該鎖定可以獲取寫入鎖定,而不需要釋放讀取鎖定。

class Program
{
  private static List<int> items = new List<int>() { 0, 1, 2, 3, 4, 5 };
  private static ReaderWriterLockSlim rwl = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

  static void ReaderMethod(object reader)
  {
    try
    {
      rwl.EnterReadLock();
      for (int i = 0; i < items.Count; i++)
      {
        Console.WriteLine("reader {0}, loop: {1}, item: {2}", reader, i, items[i]);
        Thread.Sleep(40);
      }
    }
    finally
    {
      rwl.ExitReadLock();
    }
  }

  static void WriterMethod(object writer)
  {
    try
    {
      while (!rwl.TryEnterWriteLock(50))
      {
        Console.WriteLine("Writer {0} waiting for the write lock", writer);
        Console.WriteLine("current reader count: {0}", rwl.CurrentReadCount);
      }
      Console.WriteLine("Writer {0} acquired the lock", writer);
      for (int i = 0; i < items.Count; i++)
      {
        items[i]++;
        Thread.Sleep(50);
      }
      Console.WriteLine("Writer {0} finished", writer);
    }
    finally
    {
      rwl.ExitWriteLock();
    }
  }

  static void Main()
  {
    var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
    var tasks = new Task[6];
    tasks[0] = taskFactory.StartNew(WriterMethod, 1);
    tasks[1] = taskFactory.StartNew(ReaderMethod, 1);
    tasks[2] = taskFactory.StartNew(ReaderMethod, 2);
    tasks[3] = taskFactory.StartNew(WriterMethod, 2);
    tasks[4] = taskFactory.StartNew(ReaderMethod, 3);
    tasks[5] = taskFactory.StartNew(ReaderMethod, 4);

    for (int i = 0; i < 6; i++)
    {
      tasks[i].Wait();
    }
  }
}

以上就是c# 進(jìn)程之間的線程同步的詳細(xì)內(nèi)容,更多關(guān)于c# 線程同步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論