1

複数のプロデューサーと複数のコンシューマーがいます。私の共有リソースは BlockingCollection です。ただし、私のコードは、コンシューマが 1 つしかない場合にのみ機能します。コードを実行するたびに出力が異なるため、競合状態であることはわかっています。

BlockingCollection がすべての同期などを処理すると思っていましたが、そうではありません。

すべてのプロデューサーとコンシューマー間で共有リソースを同期するにはどうすればよいですか?

これが私のコードです:

/// <summary>
/// PURE PRODUCER TYPE
/// </summary>
class Caller
{

    private BlockingCollection<Call> incommingCalls;

    public Caller(BlockingCollection<Call> calls)
    {
        incommingCalls = calls;
        //start the producer thread
        Thread thread = new Thread(new ThreadStart(placeCall));
        thread.Start();
    }

    public void placeCall()
    {
            incommingCalls.Add(myCall);
    }

}



/// <summary>
/// CONSUMER
/// </summary>
class Fresher : Employee
{

    private BlockingCollection<Call> calls;

    public Fresher(BlockingCollection<Call> incalls)
    {
        calls = incalls;
        Thread thread = new Thread(new ThreadStart(HandleCalls));
        thread.Start();

    }

    /// <summary>
    /// 
    /// </summary>
    public void HandleCalls()
    {
        while (!incommingCalls.IsCompleted)
        {
            Call item;
            if (incommingCalls.TryTake(out item, 100000))
            {
                //do something with the call

            } //else do nothing - just wait

        }


}






/// <summary>
/// 
/// </summary>
class CallCenter
{

    private BlockingCollection<Call> fresherCalls;


    private List<Caller> myCallers;
    private List<Employee> myFreshers;


    public CallCenter() 
    {
        //initial incomming calls to the fresher queue
        fresherCalls = new BlockingCollection<Call>();

        myFreshers = new List<Employee>();
        myCallers = new List<Caller>();

        generate_freshers();

        //generate to start the producer
        generate_callers();

    }

    /// <summary>
    /// 
    /// </summary>
    private void generate_freshers() 
    {
        for (int i = 0; i < 1; i++ )
        {
            myFreshers.Add(new Fresher(fresherCalls, tlCalls, locker2));
        }
    }

    /// <summary>
    /// 
    /// </summary>
    private void generate_callers() 
    {
        for (int i = 0; i < 20; i++ )
        {
            myCallers.Add(new Caller(fresherCalls, locker));
        }

    }
}
4

1 に答える 1

3

コードを実行するたびに出力が異なるため、競合状態であることがわかります。

これはマルチスレッドで一般的であり、必ずしも競合状態が原因ではありません(少なくとも悪い状態ではありません)。マルチスレッドシナリオでの注文処理は決定論的ではない傾向があり、出力が変わる可能性があります。

そうは言っても、を使用するとBlockingCollection<T>、通常、消費者を次のように書く方が簡単です。

public void HandleCalls()
{
    foreach(var item in incommingCalls.GetConsumingEnumerable())
    {
        //do something with the call
    }
}

これにより、上の任意の数のコンシューマーについて、すべての同期とチェックが処理されますBlockingCollection<T>


編集:スケジューリングを制御し、何らかの形式のラウンドロビンスケジューリングを実装する必要がある場合は、TPLのサンプル内のParallelExtensionExtrasを確認することをお勧めします。それらは、予測可能な方法で動作するインスタンスRoundRobinTaskSchedulerをスケジュールするために使用できるを提供します。Task<T>

于 2013-01-28T18:02:15.323 に答える