【问题标题】:Manage Threads Relationship in C#在 C# 中管理线程关系
【发布时间】:2011-08-23 21:14:40
【问题描述】:

现在,我正在学习多线程以及在 C# 中的使用。所以,我面临以下问题: (对不起我这么简单的问题)

假设,我们有两个名为 Producer 和 Consumer 的类。 Producer 任务在程序运行时产生 4 个数字,Consumer 任务正在消费并使用这些数字,并在程序结束时返回它们的总和。

消费者类定义:

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 类包含缓冲区变量,生产者写入此变量,消费者从中读取。我结合这些类并在我的 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# .net multithreading synchronization circular-buffer


    【解决方案1】:

    我会将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.
    

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

    【讨论】:

    • 非常感谢,这很有用:)
    【解决方案2】:

    你可以这样做

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

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

    【讨论】:

    • 是的,我使用了这样的方式,但我认为它会浪费运行时间。因为如果生产者线程产生值的速度快于消费者消费这些值的速度,那么生产者线程就会等待消费者,因为内存中没有其他位置可以放置下一个值。
    猜你喜欢
    • 1970-01-01
    • 2016-07-27
    • 2011-05-21
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多