2015-10-12 3 views
0

У меня есть 3 потока: основной поток, поток readData и поток для сбора. Из формы, когда нажата кнопка воспроизведения, начинается процесс получения устройства и потоков readData. Когда нажата кнопка «Стоп», я хочу остановить оба потока. Однако acqusitionThread.Join() блокирует выполнение. Что я делаю не так?C# Thread.Join() блокирует основной поток

Главная форма

private void btnPlay_Click(object sender, EventArgs e) 
     { 
      daqObj.Start(); 
     } 

     private void btnStop_Click(object sender, EventArgs e) 
     { 
      daqObj.Stop(); 
     } 

класс acqusition данных для считывания данных с устройства

 public void Start() 
      { 
       _isRunning = true; 

      acquisitionDevice.StartAcquisition(); //starts thread for acquisition 

       //start data acquisition thread 
       _readDataThread = new Thread(readData); 
       _readThread.Name = "Read data Thread"; 
       _redThread.Priority = ThreadPriority.AboveNormal; 
       _readThread.Start(); 
      } 

     public void ReadData() 
     { 

      try 
      { 
       // write data to file 
       while (_isRunning) 
       { 
        //Reads data (dequeues from buffer) 
        float[] data = acquisitionDevice.ReadData(numValuesAtOnce); 

        //Do other stuff with data (eg: save to file) 
       } 
       } 
       catch (Exception ex) 
       { 
        Console.WriteLine("\t{0}", ex.Message); 
       } 
    } 


      public void Stop() 
      { 
       _isRunning = false; 

        if ((_writeToFileThread != null) && _writeToFileThread.IsAlive) 
         _writeToFileThread.Join(); //stop readData thread 

        acquisitionDevice.StopAcquisition(); //stops acquisition thread 

        Console.WriteLine("acquisiton thread stopped); //THIS IS NEVER EXECUTED 

      } 

устройств класса acqusition:

public void StartAcquisition(Dictionary<string, DeviceConfiguration> deviceSerials) 
     { 
      //ensure that data acquisition is not already running 
      if (_isRunning || (_acquisitionThread != null && _acquisitionThread.IsAlive)) 
       throw new InvalidOperationException("Data acquisition is already running!"); 

      _isRunning = true; 

      //initialize buffer 
      _buffer = new WindowedBuffer<float>(BufferSizeSeconds * sampleRate * totalChannels); 

      //start data acquisition thread 
      _acquisitionThread = new Thread(DoAcquisition); 
      _acquisitionThread.Name = "DataAcquisition Thread"; 
      _acquisitionThread.Priority = ThreadPriority.Highest; 
      _acquisitionThread.Start(deviceSerials); 
     } 

public void StopAcquisition() 
     { 
      //tell the data acquisition thread to stop 
      _isRunning = false; 

      //wait until the thread has stopped data acquisition 
      if (_acquisitionThread != null) 
       _acquisitionThread.Join(); //THIS BLOCKS 

      Console.WriteLine("ended"); //THIS IS NEVER EXECUTED 
     } 

EDIT Вместо отдельной нити для чтения a, я делаю это внутри отмены токена. Я использую отдельный поток для сбора данных формируют устройство (это необходимо для непрерывного получения данных), а затем я прочитал его и записать его в файл с символическим cancellation.This это код, который работает:

public void StartAcquisition() 
      { 
       // Initialize token 
       _cancellationTokenSourceObj = new CancellationTokenSource(); 
       var token = _cancellationTokenSourceObj.Token; 

        Task.Factory.StartNew(() => 
       { 

        // Start acquisition 
        try 
        { 
         // Write device configuration parameters to .txt file 
         System.IO.StreamWriter file = new System.IO.StreamWriter(deviceConfFilePath); 
         file.WriteLine(gUSBampObj.GetDeviceConfigurationString()); 
         file.Close(); 

         // create file stream 
         using (_fileStream = new FileStream(daqFilePath, FileMode.Create)) 
         { 
          using (BinaryWriter writer = new BinaryWriter(_fileStream)) 
          { 
           // start acquisition thread 
           deviceAcquisition.StartAcquisition(); 

           // write data to file 
           while (!token.IsCancellationRequested) 
           { 
            float[] data = deviceAcquisition.ReadData(numValuesAtOnce); 

            // write data to file 
            for (int i = 0; i < data.Length; i++) 
             writer.Write(data[i]); 
           } 
          } 
         } 
        } 
        catch (Exception ex) 
        { 
         Console.WriteLine("\t{0}", ex.Message); 
        } 

       }, token) 
      .ContinueWith(t => 
      { 
       //This will run after stopping, close files and devices here 

       // stop data acquisition 
       deviceAcquisition.StopAcquisition(); 

      }); 


       } 
      } 

public void StopAcquisition() 
{ 
    _cancellationTokenSourceObj.Cancel(); 
} 
+10

Это именно то, что 'Join()' делает. Чего вы ожидаете? – SLaks

+0

Вам нужно каким-то образом прервать 'ReadData()' (который предположительно является блокирующим вызовом) – SLaks

+0

Почему он не возвращается? Почему Console.WritLine («закончился»); никогда не выполняется, если поток завершен? – nabrugir

ответ

2

Вы не хотите блокировать и ждать завершения другого потока. Это то, что делает Thread.Join().

Вместо этого вы хотите выполнить отмену потока. MSDN Managed Thread Cancellation

+0

Если я хочу писать в файл, могу ли я сделать это внутри отмена потока? Это то, что я реализовал до этого, но затем он не записывал все значения в файл. – nabrugir

+0

Вы можете делать все, что хотите. Просто убедитесь, что очистите поток и закройте, прежде чем принимать отмену. В противном случае некоторые данные не будут записаны. – bigtlb

+0

BTW: C# 4.0 в двух словах имеет очень хорошую секцию резьбы, которую сделал Джозеф Альбахари [онлайн] (http://www.albahari.com/threading/). Это очень полезный ресурс. – bigtlb

2

Thread.Join() есть, в соответствии с проектом, блокировки вызовов, от MSDN:

Регистрация представляет собой метод синхронизации, блокирует вызывающий поток (то есть поток, который вызывает метод) до тех пор, пока не будет завершен поток, который вызван методом Join. Используйте этот метод, чтобы убедиться, что поток завершен. Вызывающий абонент будет блокироваться бесконечно, если поток не завершится.

(выделено мной)

Так это дизайн, так как вы не вызвать один из перегруженных с тайм-аута. Однако у вашего кода есть другая проблема, вы можете не сигнализировать о том, что поток прекращается, как вы думаете.

Вот где volatile ключевое слово приходит, вы должны объявить isRunning поля с ним, например, так:

private volatile bool _isRunning; 

Это гарантирует, что компилятор не использует однопоточную оптимизацию кэша на значении поля, и извлекает последнее значение каждый раз, когда это поле считывается. Поскольку вы обновляете это поле из нескольких потоков, вам необходимо отметить его как volatile.

Другая проблема у вас есть ваш while цикл:

while (_isRunning) 
{ 
    //Reads data (dequeues from buffer) 
    float[] data = acquisitionDevice.ReadData(numValuesAtOnce); 

    //Do other stuff with data (eg: save to file) 
} 

Проблема эта линия:

float[] data = acquisitionDevice.ReadData(numValuesAtOnce); 

Если это вызов блокировки и ReadData не возвращается, независимо от того, то, что вы установили _isRunning, никогда не прекратит нить до тех пор, пока этот метод не вернется.

Вы должны заглянуть в Task Parallel Library и отменить задачи, сырые потоки станут обесцениваться для контроля более высокого уровня. Также рассмотрите async/await, так как вы делаете блокировку ввода-вывода, на самом деле нет причин для того, чтобы новый поток сидел и ждал ввода-вывода, когда вы можете просто ждать задачи привязки ввода-вывода.

Смежные вопросы