在c#中管理线程关系

本文关键字:线程 关系 管理 | 更新日期: 2023-09-27 18:06:24

现在,我正在学习多线程和它在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 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类主体中。

在c#中管理线程关系

我会用BlockingQueue替换HoldInteger类,您可以在这里找到实现,有关实现背后原因的更多详细信息,请查看此问题。我想。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类中,而不是有一个缓冲区(应该是微不足道的),你会达到同样的结果。

注意:使用这种方法,您不再需要担心丢失一个值或读取一个过时的值,因为线程不会在正确的时间唤醒。下面是使用"buffer"的潜在问题:

即使你的整数持有者安全地处理底层的"缓冲区",你仍然不能保证你会得到你想要的所有整数。考虑到这一点:

案例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.

由于计时器不够精确,这种事情是完全可能发生的,在第一种情况下,它会导致消费者读取一个过时的值,而在第二种情况下,它会导致消费者错过一个值。

你可以这样做

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

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