7

次のような単純な 1 プロデューサー/2 コンシューマー コードがありますが、出力C2は消費のみを示しています。コードにバグはありますか?

class Program
{
    static void Main(string[] args)
    {
        Object lockObj = new object();
        Queue<string>  queue = new Queue<string>();
        Producer p = new Producer(queue, lockObj);
        Consumer c1 = new Consumer(queue, lockObj, "c1");
        Consumer c2 = new Consumer(queue, lockObj, "c2");

        Thread t1 = new Thread(c1.consume);
        Thread t2 = new Thread(c2.consume);
        t1.Start();
        t2.Start();

        Thread t = new Thread(p.produce);
        t.Start();

        Console.ReadLine();
    } 
}
public class Producer
{
    Queue<string> queue;
    Object lockObject;
    static int seq = 0;
    public Producer(Queue<string> queue, Object lockObject)
    {
        this.queue = queue;
        this.lockObject = lockObject; 
    }

    public void produce()
    {
        while( seq++ <15) //just testinng 15 items
        {
            lock (lockObject)
            {
                string item = "item" + seq;
                queue.Enqueue(item);
                Console.WriteLine("Producing {0}", item);
                if (queue.Count == 1)
                { // first
                    Monitor.PulseAll(lockObject);
                }
            }
        }
    }

}

public class Consumer
{
    Queue<string> queue;
    Object lockObject;
    string name;
    public Consumer(Queue<string> queue, Object lockObject, string name)
    {
        this.queue = queue;
        this.lockObject = lockObject; 
        this.name = name;
    }

    public void consume()
    {
        string item;
        while (true)
        {
            lock (lockObject)
            {
                if (queue.Count == 0)
                { 
                    Monitor.Wait(lockObject);
                    continue; 
                }
                item = queue.Dequeue();
                Console.WriteLine(" {0} Consuming {1}", name, item);
            }
        }
    }
}

出力は次のとおりです。

Producing item1
 c2 Consuming item1

Producing item2
 c2 Consuming item2

Producing item3
 c2 Consuming item3

Producing item4
 c2 Consuming item4

Producing item5
 c2 Consuming item5

Producing item6
 c2 Consuming item6

Producing item7
 c2 Consuming item7

Producing item8
 c2 Consuming item8

Producing item9
 c2 Consuming item9

Producing item10
 c2 Consuming item10

Producing item11
 c2 Consuming item11

Producing item12
 c2 Consuming item12

Producing item13
 c2 Consuming item13

Producing item14
 c2 Consuming item14

Producing item15
 c2 Consuming item15
4

6 に答える 6

6

まず、問題を再現できません。ここでは、両方のスレッドがいくつかのアイテムを消費します。あなたのマシンはもっと速いと思いますが、gwが提案するようにSleepを追加することでそれを解決できます。また、プロデューサーを同期しようとしないことをお勧めします。つまり、プロデューサーをできるだけ速くキューに入れ、コンシューマーを同期させて、各アイテムを処理するユーザーを確認します。簡単に変更しましたが、正常に機能しているようです。

static void Main()
    {
        Object lockObj = new object();
        Queue<string> queue = new Queue<string>();
        Producer p = new Producer(queue);
        Comsumer c1 = new Comsumer(queue, lockObj, "c1");
        Comsumer c2 = new Comsumer(queue, lockObj, "c2");

        Thread t1 = new Thread(c1.consume);
        Thread t2 = new Thread(c2.consume);
        t1.Start();
        t2.Start();

        Thread t = new Thread(p.produce);
        t.Start();

        Console.ReadLine();
    }
}
public class Producer
{
    Queue<string> queue;
    static int seq;
    public Producer(Queue<string> queue)
    {
        this.queue = queue;
    }

    public void produce()
    {
        while (seq++ < 1000) //just testinng 15 items
        {
            string item = "item" + seq;
            queue.Enqueue(item);
            Console.WriteLine("Producing {0}", item);                
        }
    }
}

public class Comsumer
{
    Queue<string> queue;
    Object lockObject;
    string name;
    public Comsumer(Queue<string> queue, Object lockObject, string name)
    {
        this.queue = queue;
        this.lockObject = lockObject;
        this.name = name;
    }

    public void consume()
    {
        string item;
        while (true)
        {
            lock (lockObject)
            {
                if (queue.Count == 0)
                {
                    continue;
                }
                item = queue.Dequeue();
                Console.WriteLine(" {0} Comsuming {1}", name, item);
            }                
        }
    }
}

スリープを追加して、コンシューマーループを遅くすることもできます。

于 2009-09-03T03:57:41.780 に答える
4

テスト目的で、消費者コード内に時間遅延を追加してみてください。「消費」が非常に速いため、1 つの消費者スレッドが他の消費者スレッドがチャンスを得る前にキ​​ューを空にする場合があります。

(編集)

私が疑ったように、

Thread.Sleep(500);

コンシューマ スレッド内で (実行中の長い処理をシミュレートするため)、両方のスレッドが使用されます。

于 2009-09-03T03:12:57.377 に答える
2

プロデューサは、キュー カウントが 1 に等しい場合にのみ Monitor.PulseAll を呼び出します。これは、プロデューサによって実質的に何も行われていないため、それほど頻繁ではありません。これは、ゲートを通過する最初の消費スレッドが最初のアイテムをデキューすることを意味します。 2 番目の消費スレッドはキューに項目がないため、Monitor.Wait にヒットし、Pulse は (おそらく最後の項目以外がすべて残されるまで) 再び発生しないため、2 番目のスレッドはその待機状態に無限に留まります。

于 2009-09-03T03:13:42.243 に答える
0

あなたの目的は、複数の消費スレッドを「並行して」動作させることだと思います。ただし、コードの効率は低くなります。2つの消費スレッドは基本的に順番に動作しています。実際に機能するコードは、2つのコンシューマースレッドを実際に並行して実行できるように、ロックの外側に配置する必要があります。これにより、作業のプロパティに応じて、複数のコアがある場合、または単一のコアマシン上にある場合でも、ランタイムが向上します。そうでなければ、とにかく、すべての消費スレッドが順番に実行されるため、実際には複数の消費スレッドを使用する意味はありません。

于 2010-10-06T22:41:19.377 に答える
0

私はあなたのコードを実行し、c2 の消費とスパートを行う c1 のスパートを持っていました。msdn からこのリンクを確認することもできます:方法: プロデューサーとコンシューマー スレッドを同期する (C# プログラミング ガイド)

于 2009-09-03T04:09:51.617 に答える
0

Thread.Sleep(500); を追加しました。Consumer.com で

それから私は以下を持っています、

c2 Comsuming item1 c1 Comsuming item2 c2 Comsuming item3 c1 Comsuming item4 c2 Comsuming item5 c1 Comsuming item6 c2 Comsuming item7 c1 Comsuming item8 ……Sleep追加後の結果は不確かではない。

于 2009-09-03T03:39:23.767 に答える