4

そのため、2 つの異なるスレッド (thread1 と thread2) 間の通信を確立しようとするこのプログラムがあります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Project1
{
    class Class1
    {
        public static void thread1() 
        {
            Console.WriteLine("1");
            Console.WriteLine("t2 has printed 1, so we now print 2");
            Console.WriteLine("t2 has printed 2, so we now print 3");
        }

        public static void thread2()
        {
            Console.WriteLine("t1 has printed 1, so we now print 1");
            Console.WriteLine("t1 has printed 2, so we now print 2");
            Console.WriteLine("t1 has printed 3, so we now print 3");
        }

        public static void Main() {

            Thread t1 = new Thread(new ThreadStart(() => thread1()));
            Thread t2 = new Thread(new ThreadStart(() => thread2()));
            t1.Start();
            t2.Start();
            t2.Join();
            t1.Join();

        }

    }
}

ただし、次の行のように発生させたい:

Console.WriteLine("1");

...最初に実行されますが、thread2 はこの行が実行されるのを待つだけです。そのときだけ、次のように出力されます。

Console.WriteLine("t1 has printed 1, so we now print 1");

この行が印刷された後、次の行が表示されます。

Console.WriteLine("t2 has printed 1, so we now print 2");

...印刷するなど。したがって、コードを変更して、スレッドが相互に通信し、行が次の順序で印刷されるようにします。

            Console.WriteLine("1"); // from t1
            Console.WriteLine("t1 has printed 1, so we now print 1"); // from t2
            Console.WriteLine("t2 has printed 1, so we now print 2"); // from t1
            Console.WriteLine("t1 has printed 2, so we now print 2"); // from t2
            Console.WriteLine("t2 has printed 2, so we now print 3"); // from t1
            Console.WriteLine("t1 has printed 3, so we now print 3"); // from t2

ロックの機能は理解していますが、2 つの異なるスレッドが同じ関数で実行されている場合にのみ適用されます。ただし、ここでは 2 つの機能が異なるため、ここではロックを使用できません。

何か案は?

4

4 に答える 4

10

Monitor.WaitMonitor.Pulseが必要なようです。Threading に関する無料の eBookがあります (おそらくたくさんありますが、これが役に立ちました)。

静的オブジェクトを使用してロックし、スレッドを呼び出しMonitor.Pulseて、「自分のターンが終了した」ことを通知し、「Monitor.Wait次のターンを待つ」ことができます。基本コードを使用した実装例を次に示します。

public class Class1
{
     // Use this to syncrhonize threads
    private static object SyncRoot = new object();
     // First "turn" goes to thread 1
    private static int threadInControl = 1;

    public static void thread1()
    {
        lock(SyncRoot) // Request exclusive access to SyncRoot
        {
            Console.WriteLine("1");
            GiveTurnTo(2); // Let thread 2 have a turn
            WaitTurn(1);   // Wait for turn to be given to thread 1
            Console.WriteLine("t2 has printed 1, so we now print 2");
            GiveTurnTo(2); // Let thread 2 have a turn
            WaitTurn(1);   // Wait for turn to be given to thread 1
            Console.WriteLine("t2 has printed 2, so we now print 3");
            GiveTurnTo(2); // Let thread 2 have a turn
        }
    }

    public static void thread2()
    {
        lock(SyncRoot) // Request exclusive access to SyncRoot
        {
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 1, so we now print 1");
            GiveTurnTo(1); // Let thread 1 have a turn
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 2, so we now print 2");
            GiveTurnTo(1); // Let thread 1 have a turn
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 3, so we now print 3");
            GiveTurnTo(1); // Let thread 1 have a turn
        }
    }

    // Wait for turn to use SyncRoot object
    public static void WaitTurn(int threadNum)
    {
        // While( not this threads turn )
        while (threadInControl != threadNum)
        {
            // "Let go" of lock on SyncRoot and wait utill 
            // someone finishes their turn with it
            Monitor.Wait(SyncRoot);
        }
    }

    // Pass turn over to other thread
    public static void GiveTurnTo(int nextThreadNum)
    {
        threadInControl = nextThreadNum;
        // Notify waiting threads that it's someone else's turn
        Monitor.Pulse(SyncRoot);
    }

    public static void  void Main()
    {
        Thread t1 = new Thread(new ThreadStart(() => Class1.thread1()));
        Thread t2 = new Thread(new ThreadStart(() => Class1.thread2()));
        t1.Start();
        t2.Start();
        t2.Join();
        t1.Join();
    }
}

lock キーワードの使用に関しては、同じ関数内の同期に限定されません。ロックは、単一スレッドへのリソース (オブジェクト) への排他的アクセスを「保証」します (排他的とは、一度に 1 つのスレッドのみがそのリソースのロックを取得できることを意味します。ロックは、他のスレッドが単にオブジェクト自体にアクセスすることを妨げません)。 .

簡単に言うとlock(someObject)、スレッドが使用するために列に並び、someOjectその前にある他のすべてのスレッドが順番を終えるまで待機してから続行するようなものです。スレッドは、lock ステートメントのスコープを離れると、その「ターン」を終了します (Monitor.Pulseまたは などを追加しない限りMonitor.Wait)。

于 2012-08-16T15:48:20.700 に答える
3

ManualResetEventを使用します。スレッド2で待機し、Console.WriteLn() http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspxの後でthread1に設定します。

于 2012-08-16T15:35:32.600 に答える
2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Project1
{
    class Class1
    {
        private static ManualResetEvent mre1 = new ManualResetEvent(false);
        private static ManualResetEvent mre2 = new ManualResetEvent(false);

        public static void thread1() 
        {
            Console.WriteLine("1");
            mre2.Set();
            mre1.WaitOne();
            Console.WriteLine("t2 has printed 1, so we now print 2");
            mre2.Set();
            mre1.WaitOne();
            Console.WriteLine("t2 has printed 2, so we now print 3");
        }

        public static void thread2()
        {
            mre2.WaitOne();
            Console.WriteLine("t1 has printed 1, so we now print 1");
            mre1.Set();
            mre2.WaitOne();
            Console.WriteLine("t1 has printed 2, so we now print 2");
            mre1.Set();
            mre2.WaitOne();
            Console.WriteLine("t1 has printed 3, so we now print 3");
        }

        public static void Main() {

            Thread t1 = new Thread(new ThreadStart(() => thread1()));
            Thread t2 = new Thread(new ThreadStart(() => thread2()));

            t1.Start();
            t2.Start();

            while (true) {
                Thread.Sleep(1);
            }

        }

    }
}

みんなありがとう。私は今、解決策を持っていると思います!

于 2012-08-16T16:00:36.240 に答える
1

Joe Albahari 氏による Advanced Threading チュートリアルに含まれる、Wait および Pulse を使用したシグナリング セクションのTwo-Way Signaling and Racesセクションにある例から:

static readonly object locker = new object();
private static bool ready, go;

public static void Thread1()
{
  IEnumerable<Action> actions = new List<Action>()
  {
   () => Console.WriteLine("1"),
   () => Console.WriteLine("t2 has printed 1, so we now print 2"),
   () => Console.WriteLine("t2 has printed 2, so we now print 3")
  };

  foreach (var action in actions)
  {
    lock (locker)
    {
      while (!ready) Monitor.Wait(locker);
      ready = false;
      go = true;
      Monitor.PulseAll(locker);
      action();
    } 
  }
}

public static void Thread2()
{
  IEnumerable<Action> actions = new List<Action>()
  {
   () => Console.WriteLine("t1 has printed 1, so we now print 1"),
   () => Console.WriteLine("t1 has printed 2, so we now print 2"),
   () => Console.WriteLine("t1 has printed 3, so we now print 3")
  };

  foreach (var action in actions)
  {
    lock (locker)
    {
      ready = true;
      Monitor.PulseAll(locker);
      while (!go) Monitor.Wait(locker);
      go = false;
      action();
    }
  }
}

private static void Main(string[] args)
{
  Thread t1 = new Thread(new ThreadStart(() => Thread1()));
  Thread t2 = new Thread(new ThreadStart(() => Thread2()));
  t1.Start();
  t2.Start();
  t2.Join();
  t1.Join();
}
于 2012-08-16T16:01:48.877 に答える