2011-08-23 88 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

是的,我用这种方式,但我认为它可以浪费运行时间。因为如果生产者线程产生的值快于消费者可以使用这些值,那么生产者线程会等待消费者,因为内存中没有其他位置可以放置下一个值。 –