在C#中管理线程关系

时间:2011-08-23 21:14:40

标签: c# .net multithreading synchronization circular-buffer

现在,我正在学习多线程并在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;
        }
    }
}

和Producer Class的定义如下:

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类包含缓冲区变量,生产者编写此变量并从中读取消费者。我将这些类组合在一起,并在我的main方法中编写以下代码:

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类主体中。

感谢您的关注。

2 个答案:

答案 0 :(得分:4)

我会将HoldInteger类替换为BlockingQueueyou can find an implementation here,并详细了解实施背后的原因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.

由于计时器不够精确,这种情况完全有可能,并且在第一种情况下它将导致消费者读取陈旧值,而在第二种情况下,它将导致消费者错过一个值。 / p>

答案 1 :(得分: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;
           }
       }
    }
}

注意我还没有测试过这段代码!