2011-12-01 2 views
1

개요를 청취 루프 작업자 스레드 :C#을하는 동안 명령 및 응답 문제의

나는 사용자의 명령을 듣고 스레드 (while..loop)를 사용하려고합니다. 사용자가 명령을 보내면 클래스 (LoopingWorkerThread)에있는 전역 변수에 새 값이 지정됩니다.

스레드 수면 시간 값을 10 밀리 초보다 낮게 설정하지 않으면 이해가되지 않고 응답이 없습니다 (ListenCommand 메서드에 있음). 전역 매개 변수가 메서드에서 "_CommandReceived"를 덮어 쓰는 것처럼 보입니다. 아마도 프로세서가 빠르게 실행되고 매개 변수의 값이 변경되었습니다 ("_CommandReceived").

더 좋은 메커니즘이 있는지 여부를 친절히 설명합니다. ListenCommand while 루프에서 잠급니다.

다음

은 코드입니다 :

public class LoopingWorkerThread 
    { 
     /// <summary> 
     /// Local main thread for LoopingWorkerThread 
     /// </summary> 
     private Thread t; 
     /// <summary> 
     /// Local parameter to identify the LoopingWorkerThread Is On 
     /// </summary> 
     private bool _IsOn; 
     /// <summary> 
     /// Local parameter to store command received from user 
     /// </summary> 
     private int _CommandReceived; 
     /// <summary> 
     /// Local object to use for locking the LoopingWorker Thread 
     /// </summary> 
     private object _LockListenCommand = new object(); 
     /// <summary> 
     /// Properties of LoopingWorker Thread Is On 
     /// </summary> 
     public bool IsOn 
     { 
      get { return _IsOn; } 
      set { _IsOn = value; } 
     } 
     /// <summary> 
     /// Property of storing the command received from user 
     /// </summary> 
     public int CommandReceived 
     { 
      get { return _CommandReceived; } 
      set { _CommandReceived = value; } 
     } 
     /// <summary> 
     /// Delegate for OnResponse Event Handler 
     /// </summary> 
     /// <param name="sender"></param> 
     /// <param name="e"></param> 
     public delegate void OnResponseHandler(object sender, ResponseArg e); 
     /// <summary> 
     /// Event of OnResponse 
     /// </summary> 
     public event OnResponseHandler OnResponse; 
     /// <summary> 
     /// Constructor of LoopingWorkerThread Class 
     /// </summary> 
     public LoopingWorkerThread() 
     { 
      _IsOn = false; 
     } 
     /// <summary> 
     /// Method of LoopingWorkerThread Function 
     /// </summary> 
     private void ListenCommand() 
     { 
      lock (_LockListenCommand) 
       while (_IsOn) 
       { 
        switch (_CommandReceived) 
        { 
         case 0: 
          // Ignore default command 
          break; 
         case 1: 
          FireOnResponse("Received cmd 1, response [Hello One]"); 
          break; 
         case 2: 
          FireOnResponse("Received cmd 2, response [Hello Two]"); 
          break; 
         default: 
          FireOnResponse("Error. Received unidentified command - " + _CommandReceived.ToString()); 
          break; 
        } 

        //Console.WriteLine("ThreadProc: Cmd:[{0}] - Response:{1}", _CommandReceived.ToString(), ReaderResponse); 

        // Reset or Clear the Command Received 
        _CommandReceived = 0; 

        // If the sleep less than 10 millisecond, it always don't catch the 
        // command received which assigned to 1 or 2. Don't understand, or is there 
        // any better method. 
        **Thread.Sleep(10);** 
       } 
     } 
     /// <summary> 
     /// Function of firing response event back to user 
     /// </summary> 
     /// <param name="message"></param> 
     private void FireOnResponse(string message) 
     { 
      ResponseArg myarg = new ResponseArg(message); 
      if (OnResponse != null) 
       OnResponse(this, myarg); 
     } 
     /// <summary> 
     /// Method of starting the LoopingWorkerThread 
     /// </summary> 
     public void Start() 
     { 
      _IsOn = true; 

      FireOnResponse("Main thread: Started."); 

      // The constructor for the Thread class requires a ThreadStart 
      // delegate that represents the method to be executed on the 
      // thread. C# simplifies the creation of this delegate. 
      t = new Thread(new ThreadStart(ListenCommand)); 

      // Start ThreadProc. Note that on a uniprocessor, the new 
      // thread does not get any processor time until the main thread 
      // is preempted or yields. Uncomment the Thread.Sleep that 
      // follows t.Start() to see the difference. 
      t.Start(); 

      //Thread.Sleep(0); 

      FireOnResponse("Main thread: Call Start()."); 

     } 
     /// <summary> 
     /// Method of stopping the LoopingWorkerThread 
     /// </summary> 
     public void Stop() 
     { 
      _IsOn = false; 
      t.Join(); 
      //t.Abort(); 

      FireOnResponse("LoopingWorker Thread is stopped."); 
     } 
     /// <summary> 
     /// Method of sending command to the LoopingWorkerThread 
     /// </summary> 
     /// <param name="readercmd"></param> 
     public void SendCommand(int readercmd) 
     { 
      _CommandReceived = readercmd; 
     } 
    } 
+0

활성 스레드가 하나 이상 존재하지 않는 이유는 무엇입니까? _LockLisenCommand'를 잠그는 이유는 무엇입니까? – ebb

+0

내 수업에만 하나의 스레드가 실행 중이므로 잠글 필요가 없다는 것을 의미합니까? 네, 필요 없다고 생각합니다. – Fusionmate

+0

어떤 버전의 .NET을 사용하고 있습니까? – ebb

답변

1

변수 휘발성를 선언하십시오. 이에 대한 자세한 내용 http://msdn.microsoft.com/en-us/library/x13ttww7.aspx

+0

Great Prafulla. 그것은 효과가 있었다. 스레드 슬립 값을 0 밀리 초로 설정해도 올바르게 응답했습니다. 스레드 수면에 댓글을 달았다면 응답하지 않습니다. 어쨌든, 이것은 나에게 위대하다. – Fusionmate

+0

안녕하세요 Prafulla, 내 보내기 단추를 클릭하면 SendCommand 메서드를 10 번 실행하려면 루프를 넣으면 내 Windows 양식에서 시도한; 나는 오직 하나의 반응만을 얻었고, 나의 기대 결과는 10 반응이다. 그러나 내가 수동으로 10 번 클릭하면 10 개의 응답을 얻습니다. 왜 그런지 알아? – Fusionmate

4

Thread.Sleep produces the necessary memory barrier_commandReceived을 올바르게 읽어야하므로 코드가 작동합니다. Thread.Sleep 호출을 제거하면 암시적인 메모리 장벽이 제거됩니다. 분명히, 이것은 의존하는 좋은 메커니즘이 아닙니다.

더 중요한 것은 당신이 잘못된 길로 가고 있다는 것입니다. 당신이 사용해야하는 것은 생산자 - 소비자 패턴입니다. 대기열이 비어있는 동안 Take에서 소비자를 차단하기 때문에 BlockingCollection 클래스를 사용하면 매우 쉽습니다.

public class Example 
{ 
    private BlockingCollection<int> commands = new BlockingCollection<int>(); 

    public Example() 
    { 
    var thread = new Thread(Run); 
    thread.IsBackground = true; 
    thread.Start(); 
    } 

    public void SendCommmand(int command) 
    { 
    commands.Add(command); 
    } 

    private void Run() 
    { 
    while (true) 
    { 
     int command = commands.Take(); 
     ProcessCommand(command);  
    } 
    } 

    private void ProcessCommand(int command) 
    { 
    // Process the command here. 
    } 
} 

BlockingCollectionReactive Extensions 다운로드의 일부로서 3.5을 사용할 수있다.

+0

그냥 생각 : 당신은'명령'에'null '을 추가하는'Stop' 메쏘드를 만들 수 있고'Run' 메쏘드에서 조건 체크를 가질 수 있습니다. 'command'가'null'과 같은 경우, while 루프를 벗어납니다. – ebb

+0

@ebb : 네,'int' 대신'int?'를 써야합니다.하지만 그래, 좋은 생각입니다. –

+0

그레이트 브라이언. 저는 여기서 귀중한 교훈을 얻었습니다. – Fusionmate