2011-08-23 34 views
2

現在,我正在學習C#中的多線程和使用它。所以,我面臨的問題如下: (對不起,我這麼簡單的問題)在C#中管理線程關係#

假設我們有兩個名爲Producer和Consumer的類。生產者任務正在生成4個數字,而程序運行和消費者任務正在消耗並使用這些數字,並在程序結束時返回它們的總和。

消費類定義:

class Consumer 
{ 
    private HoldInteger sharedLocation; 
    private Random randomSleepTime; 

    public Consumer(HoldInteger shared, Random random) 
    { 
     sharedLocation = shared; 
     randomSleepTime = random; 
    } 

    public void Consume() 
    { 
     int sum = 0; 

     for (int i = 1; i <= 4; i++) 
     { 
      Thread.Sleep(randomSleepTime.Next(1, 3000)); 
      sum += sharedLocation.Buffer; 
     } 
    } 
} 

和製作類的定義如下:

class Producer 
{ 
    private HoldInteger sharedLocation; 
    private Random randomSleepTime; 

    public Producer(HoldInteger shared, Random random) 
    { 
     sharedLocation = shared; 
     randomSleepTime = random; 
    } 

    public void Produce() 
    { 
     for (int i = 1; i <= 4; i++) 
     { 
      Thread.Sleep(randomSleepTime.Next(1, 3000)); 
      sharedLocation.Buffer = i; 
     } 
    } 
} 

而且,我們已經HoldInteger類包含緩衝變量,生產者寫這個變量和消費者讀從那。我結合這些類和程序我的主要方法,下面的代碼:

static void Main(string[] args) 
{ 
    HoldInteger holdInteger = new HoldInteger(); 
    Random random = new Random(); 

    Producer producer = new Producer(holdInteger, random); 

    Consumer consumer = new Consumer(holdInteger, random); 

    Thread producerThread = new Thread(new ThreadStart(producer.Produce)); 
    producerThread.Name = "producer"; 

    Thread consumerThread = new Thread(new ThreadStart(consumer.Consume)); 
    consumerThread.Name = "consumer"; 

    producerThread.Start(); 
    consumerThread.Start(); 
} 

所以,我的問題是,How can i manage this relationship With Low Memory and Time Wasting ?

請注意,這些線程管理代碼將被放置在HoldInteger類主體。

感謝您的關注。

回答

4

我會用BlockingQueue,you can find an implementation here替換HoldInteger類,關於實現背後原因的更多詳細信息,請參閱check this question。我認爲.NET 4.0也可能有阻塞隊列。這種方法將隨後讓事情變得更容易管理:

class Producer 
{ 
    //... 

    public void Produce() 
    { 
     for (int i = 1; i <= 4; i++) 
     { 
      Thread.Sleep(randomSleepTime.Next(1, 3000)); 
      blockingIntQueue.Enqueue(i); 
     } 
    } 
} 

你的消費者會像現在這樣:

class Consumer 
{ 
    //... 

    public void Consume() 
    { 
     int value = 0; 
     for (int i = 1; i <= 4; i++) 
     { 
      if(blockingIntQueue.TryDequeue(out value)) 
      { 
       sum += value; 
      } 
     } 
    } 
} 

不過,如果你想保持HoldInteger(如果這是某種形式的需求),那麼你可以將阻塞隊列放在HoldIntegerUnsynchronized類中,而不是有一個緩衝區(應該很簡單),你將獲得相同的結果。

注意:使用這種方法,您不再需要擔心丟失值或讀取陳舊值,因爲線程不會在恰當的時間醒來。這裏是潛在的問題與使用「緩衝」:

即使你的整數持卡者不能處理潛在的「緩衝」安然,你仍然不能保證你能得到你想要的整數。考慮到這一點:

案例1

Producer wakes up and writes integer. 
Consumer wakes up and reads integer. 

Consumer wakes up and reads integer. 
Producer wakes up and writes integer. 

案例2

Consumer wakes reads integer. 
Producer wakes up and writes integer. 

Producer wakes up and writes integer. 
Consumer wakes up and reads integer. 

由於計時器不夠精確,這樣的事情是完全可能的,並在第一它會導致消費者讀取陳舊的價值,而在第二種情況下,它會導致消費者錯過價值。

+0

非常感謝,這是非常有用的:) –

+0

@Hossein,盡情享受吧! :) – Kiril

1

你可以做這樣的事情

class HoldIntegerUnsynchronized { 
    int buffer; 
    object syncLock = new object(); 
    bool goodToRead = false; 
    bool goodToWrite = true; 

    public int Buffer { 
     get { 
      lock (syncLock) { 
       while (!goodToWrite) 
        Monitor.Wait(syncLock); 
       buffer = value; 
       goodToWrite = false; 
       goodToRead = true; 
       Monitor.Pulse(syncLock); 
      } 
     } 
     set { 
      lock (syncLock) { 
       while (!goodToRead) 
        Monitor.Wait(syncLock); 
       int toReturn = buffer; 
       goodToWrite = true; 
       goodToRead = false; 
       Monitor.Pulse(syncLock); 
       return toReturn; 
      } 
     } 
    } 
} 

注意我沒有測試此代碼!

+0

是的,我用這種方式,但我認爲它可以浪費運行時間。因爲如果生產者線程產生的值快於消費者可以使用這些值,那麼生產者線程會等待消費者,因爲內存中沒有其他位置可以放置下一個值。 –