在Python中停止执行线程。线程在join方法之前被阻塞

时间:2014-06-27 13:00:55

标签: python windows multithreading wxpython stdout

我正在编写在新进程中执行子程序的程序,并在单独的线程中读取这些程序的标准输出并将其写入StyledTextCtrl控件。我遇到了停止执行线程的问题。我有以下代码:

import subprocess
from threading import Thread, Lock
import wx
import wx.stc


import logging
logging.basicConfig(level=logging.DEBUG,
                    format='[%(levelname)s] (%(threadName)-10s) %(module)10s:%(funcName)-15s %(message)s',
                    )

class ReadThread(Thread):
    def __init__(self, subp, fd, append_text_callback):
        Thread.__init__(self)
        self.killed = False
        self.subp = subp
        self.fd = fd
        self.append_text_callback = append_text_callback

    def run(self):
        chunk = None
        while chunk != '' and not self.killed:
            self.subp.poll()
            chunk = self.fd.readline()
            if chunk:
                self.append_text_callback('%d: ' % self.subp.pid + chunk)
            if chunk == "":
                break
        logging.debug('END')


class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Example1")
        panel = wx.Panel(self, wx.ID_ANY)
        self.log = wx.stc.StyledTextCtrl(panel, wx.ID_ANY, size=(300,100), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        run_button = wx.Button(panel, wx.ID_ANY, 'Run')
        self.Bind(wx.EVT_BUTTON, self.onRun, run_button)
        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.log, 1, wx.ALL|wx.EXPAND, 5)
        sizer.Add(run_button, 0, wx.ALL|wx.CENTER, 5)
        panel.SetSizer(sizer)
        self.read_thread_1 = None
        self.read_thread_2 = None
        self.log_lock = Lock()            

    def OnCloseFrame(self, event):
        logging.debug('CLOSE')
        if self.read_thread_1:
            self.read_thread_1.killed = True
        if self.read_thread_2:
            self.read_thread_2.killed = True
        if self.read_thread_1:
            self.read_thread_1.join(timeout=1)
            logging.debug('after 1 join')
        if self.read_thread_2:
            self.read_thread_2.join(timeout=1)
            logging.debug('after 2 join')
        self.read_thread_1 = None
        self.read_thread_2 = None
        event.Skip()

    def onRun(self, event):
        cmd1 = "E:/threading_block/stdout_emulator.exe 50 500"
        subp1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        self.read_thread_1 = ReadThread(subp1, subp1.stdout, self.AppendText)
        self.read_thread_1.start()
        cmd2 = "E:/threading_block/stdout_emulator.exe 21 400"
        sp2 = subprocess.Popen(cmd2, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        self.read_thread_2 = ReadThread(sp2, sp2.stdout, self.AppendText)
        self.read_thread_2.start()
        logging.debug('END')

    def AppendText(self, text):
        self.log_lock.acquire()
        logging.debug('%s' % text)
        self.log.AddText(text)
        self.log_lock.release()

if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

我在OnCloseFrame方法中停止了线程。我在killed值中设置了True属性,因此它导致在while类的run方法中完成ReadThread循环的执行。 在执行线程期间关闭此应用程序(在Windows下)时没有问题。但是,如果我从timeout=1self.read_thread_1.join(timeout=1)删除self.read_thread_2.join(timeout=1),则会阻止该计划。

该程序在self.read_thread_1.join()方法中OnCloseFrame之前停滞不前。

这种阻止的问题是什么?为什么我应该timeout使用join

std_emulator.exe的C代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>

int main(int argc, char *argv[])
{
  int s;
  int n;
  if (argc > 1)
  {    
    s = atoi(argv[1]);
    n = atoi(argv[2]);
  }
  else
  {
    s = 1;
    n = 50;
  }

  int i;
  for (i = s; i < n; ++i)
  {
    printf("This is %d line\n", i);
    fflush(stdout);
    if (i % 2 == 0) 
    {       
      srand(time(NULL));
      int r = rand() % 7;
      printf("sleep_type_2 with %d\n", r);
      Sleep(30 * r);
      fflush(stdout); 
    }     
      if (i % 3 == 0) 
      {     
        int r = rand() % 7;
        printf("sleep_type_3 with %d\n", r);
        Sleep(40 * r);
        fflush(stdout); 
      }
      if (i % 5 == 0) 
      {
        int r = rand() % 9;
        printf("sleep_type_5 with %d\n", r);
        Sleep(50);
        fflush(stdout); 
      }   
    }
}

1 个答案:

答案 0 :(得分:1)

在你的情况下,当你使用join(timeout=1)时,你实际上并没有杀死线程。相反,join操作正在等待一秒钟以查看线程是否退出,而当它没有退出时,它就会放弃。 join无法正常工作,因为您的帖子很可能会阻止此行:

chunk = self.fd.readline()

所以它永远不会循环,看看killed是否已设置为True。我认为干净地关闭线程的最佳方法是终止在ReadThreadOnCloseFrame中运行的子进程:

if self.read_thread_1:
    self.read_thread_1.subp.terminate()
    self.read_thread_1.join()
if self.read_thread_2:
    self.read_thread_2.subp.terminate()
    self.read_thread_2.join()

终止子进程将取消阻止readline调用,并允许您干净地终止该线程。