在 C# 中管理线程关系

发布于 2024-11-30 19:59:47 字数 1879 浏览 3 评论 0原文

现在,我正在学习多线程及其在 C# 中的用法。所以,我面临如下问题: (抱歉我的问题如此简单)

假设我们有两个名为 Producer 和 Consumer 的类。生产者任务在程序运行时生成 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;
        }
    }
}

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

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

所以,我的问题是如何管理这种与低内存和浪费时间的关系?

请注意,这些线程管理代码将是放置在 HoldInteger 类主体中。

感谢您的关注。

Now, i am learning multi-threading and usage of it in C#. So, i face the problem as below:
(Sorry for my so simple question)

Suppose that, we have two classes named Producer and Consumer. Producer task is producing 4 numbers while program running and Consumer task is consuming and using those numbers and return the sum of them at the end of program.

Consumer Class definition:

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

and the definition of Producer Class is as below :

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

And also, we have HoldInteger class contains Buffer variable that producer write this variable and consumer read from that. I combine these classes and program the below code in my main method:

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

So, my question is that How can i manage this relationship With Low Memory and Time Wasting ?

Please note that, these threads management code will be placed in HoldInteger class body.

Thanks for your attention.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吃不饱 2024-12-07 19:59:47

我将用 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 类中使用阻塞队列而不是使用缓冲区(应该很简单),您将获得相同的结果。

注意:使用这种方法,您不再需要担心丢失值或读取过时的值,因为线程不会在正确的时间唤醒。这是使用“缓冲区”的潜在问题:

即使您的整数持有者确实安全地处理了底层“缓冲区”,您仍然不能保证您将获得您想要的所有整数。考虑到这一点:

情况 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.

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

I would replace the HoldInteger class with a BlockingQueue, you can find an implementation here and for more details on the reason behind the implementation, check this question. I think .NET 4.0 might have a blocking queue too. This approach will subsequently make things a lot easier to manage:

class Producer
{
    //...

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

Your consumer will look like this now:

class Consumer
{
    //...

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

However, if you want to keep the HoldInteger (if this is some sort of requirement), then you can place the blocking queue inside the HoldIntegerUnsynchronized class instead of having a buffer (should be trivial to do) and you will achieve the same result.

Note: with this approach you no longer have to worry about missing a value or reading a stale value because the threads don't wake up at exactly the right time. Here is the potential problem with using a "buffer":

Even if your integer holder does handle the underlying "buffer" safely, you are still not guaranteed that you will get all the integers that you want. Take this into consideration:

Case 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.

Case 2

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

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

Since the timer is not precise enough, this sort of thing is entirely possible and in the first case it will cause the consumer to read a stale value, while in the second case it will cause the consumer to miss a value.

草莓酥 2024-12-07 19:59:47

您可以执行类似

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

“注意我尚未测试此代码!”之类的操作!

You can do something like

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

Note I haven't tested this code!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文