目录

Python多线程开发学习笔记

前言:

  • 我们在前面学习了Python 多进程开发相关的知识。
  • 我们知道,多任务我们可以通过多进程完成,当然你也可以在一个进程中创建多个线程来完成。
  • 本次我们将学习Python 多线程开发相关的知识。

多进程与多线程的不同点

  • 多进程中,同一个变量,每个进程都有一份拷贝存在于每个进程中,进程间互不影响,
  • 多线程中,所有变量都由所有线程共享。所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,造成数据错乱。

Python中的多线程

  • Python的标准库提供了两个模块:

  • 1、_thread :低级模块。

  • 2、threading :高级模块,对_thread进行来封装,绝大部分情况下我们只需要使用该模块即可

  • 示例代码如下:

      import time,threading,random;
    
      def run_thread():
      	print('current thread %s is running。。。' % threading.current_thread().name)
      	i = 0
        while i < 3:
      		i += 1
        print('current thread %s ,value is %s' % (threading.current_thread().name, i))
      		time.sleep(random.random())
      	print('current thread %s ended...' % threading.current_thread().name)
    
      print('current thread %s is running...' % threading.current_thread().name)
      t = threading.Thread(target=run_thread, )
      t.start()
      t.join()
      print('current thread %s ended.' % threading.current_thread().name)
    
  • 运行结果如下:

      current thread MainThread is running...
      current thread Thread-1 is running。。。
      current thread Thread-1 ,value is 1
      current thread Thread-1 ,value is 2
      current thread Thread-1 ,value is 3
      current thread Thread-1 ended...
      current thread MainThread ended.
    
  • 结果解析:

  • 可以看到,我们当前的线程名为 MainThread,创建的线程为Thread-1.

  • 为什么默认线程教MainThread呢?

  • 因为每个进程创建时都会默认创建一个子线程,该线程由Python命名为MainThread。

  • 当然我们自己创建的线程也可以自己命名,通过传入参数name=xxxx即可,如果不传入,则默认从Thread-1开始后面的数字递增。

  • 如此我们就完成了多线程的创建及调用

多线程并发产生的问题

  • 多线程很容易产生数据错误的问题,因为在同一进程中的变量对于该进程内的线程来说,都是共享的,一旦多个线程同时修改相同的变量,问题就来了。

  • 我们看的示例代码(希望每个线程加1后输出,理想下是输出 1 2 3):

      import time,threading,random;
    
      t_v = 0;
    
      def run_thread():
      	global t_v;
      	t_v+=1;
      	time.sleep(random.random())
      	print('current thread %s is running...'% threading.current_thread().name)
      	print('current thread %s ended. value is %d' % (threading.current_thread().name,t_v));
    
      print('current thread %s is running...' % threading.current_thread().name)
      t1 = threading.Thread(target=run_thread, )
      t2 = threading.Thread(target=run_thread, )
      t3 = threading.Thread(target=run_thread, )
    
      t1.start()
      t2.start()
      t3.start()
    
      t1.join()
      t2.join()
      t3.join()
      print('current thread %s ended.' % threading.current_thread().name)
    
  • 运行结果如下:

      current thread MainThread is running...
      current thread Thread-3 is running...
      current thread Thread-3 ended. value is 3
      current thread Thread-2 is running...
      current thread Thread-2 ended. value is 3
      current thread Thread-1 is running...
      current thread Thread-1 ended. value is 3
      current thread MainThread ended.
    
  • 结果分析:然而,最后输出的都是3,为什么呢?因为三个线程加1后都随机休眠了一定时间才输出,这样,在输出前,t_v被加了三次。所以最后输出的都是3

  • 那么我们该如何解决呢?

  • 当然是So Easy了。

Lock

  • 众多支持多进程、线程开发的编程语言都会有引入锁这一概念去去处理共享问题,

  • Ok,我们来看看Python中的锁是如何使用的呢?

  • 代码修改如下(在操作t_v前对当前线程上锁,输出后再释放):

      import time,threading,random;
    
      t_v = 0;
      lock = threading.Lock();
      def run_thread():
      	try:
      		lock.acquire();
      		global t_v;
      		t_v+=1;
      		time.sleep(random.random())
      		print('current thread %s is running...'% threading.current_thread().name)
      		print('current thread %s ended. value is %d' % (threading.current_thread().name,t_v));
      	except Exception as e:
      		print(e)
      	finally:
      		lock.release();
    
      print('current thread %s is running...' % threading.current_thread().name)
      t1 = threading.Thread(target=run_thread, )
      t2 = threading.Thread(target=run_thread, )
      t3 = threading.Thread(target=run_thread, )
    
      t1.start()
      t2.start()
      t3.start()
    
      t1.join()
      t2.join()
      t3.join()
      print('current thread %s ended.' % threading.current_thread().name)
    
  • 结果输出为:

      current thread MainThread is running...
      current thread Thread-1 is running...
      current thread Thread-1 ended. value is 1
      current thread Thread-2 is running...
      current thread Thread-2 ended. value is 2
      current thread Thread-3 is running...
      current thread Thread-3 ended. value is 3
      current thread MainThread ended.
    
  • OK,问题就这么简单的解决了。

  • 使用 try finally是保证锁最后一定会被释放,一定要注意哦,如果不释放就会产生死锁了。