并发编程之多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程之多线程相关的知识,希望对你有一定的参考价值。

一、并发编程之多线程

1、线程简单介绍

进程是资源单位,把所有资源集中到一起,而线程是执行单位,真正执行的是线程

每个进程都有一个地址空间,而且默认就有一个控制线程

多线程:在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间。进程之间是竞争关系,线程之间是协作关系

线程的创建开销比进程小很多,运行较快

主线程从执行层面上代表了其所在进程的执行过程

 

2、线程开启方式

方式一:使用替换threading模块提供的Thread

 from threading import Thread
 
 def task():
   print(‘is running‘)

 if __name__ == ‘__main__‘:
   t=Thread(target=task,)
   t.start()
   print(‘主‘)

 

方式二:自定义类,继承Thread

from threading import Thread
class MyThread(Thread):
  def __init__(self,name):
    super().__init__()
    self.name=name
  def run(self):
    print(‘%s is running‘ %self.name)

if __name__ == ‘__main__‘:
  t=MyThread(‘egon‘)
  t.start()
  print(‘主‘)

 

3、线程与进程的pid

from threading import Thread
from multiprocessing import Process
import os

def task():
  print(‘%s is running‘ %os.getpid())

if __name__ == ‘__main__‘:
  # t1=Thread(target=task,)      #在主进程下开启多个线程,每个线程都跟主进程的pid一样
  # t2=Thread(target=task,)
  t1=Process(target=task,)     #开启多个进程,每个进程都有不同的pid
  t2=Process(target=task,)
  t1.start()
  t2.start()
  print(‘主‘,os.getpid())

 

4、多线程共享同一个进程内的资源

from threading import Thread
from multiprocessing import Process
n=100
def work():
  global n
  n=0

if __name__ == ‘__main__‘:

  # p=Process(target=work,)        
  # p.start()
  # p.join()
  # print(‘主‘,n)                 #子进程p已经将自己的全局的n改成了0,但改的是它自己的,父进程仍然是100

  t=Thread(target=work,)
  t.start()
  t.join()
  print(‘主‘,n)                    #查看结果为0,同一进程内的线程之间共享进程内的数据

 

5、Thread对象其他相关的属性或方法

isAlive():返回线程是否活动

getName():返回线程名

setName():设置线程名

 current_thread的用法
 from threading import Thread,activeCount,enumerate,current_thread
 from multiprocessing import Process
 import time

 def task():
   print(‘%s is running‘ %current_thread().getName())
   time.sleep(2)

 if __name__ == ‘__main__‘:
   p=Process(target=task)
   p.start()
   print(current_thread())

 

6、守护线程

进程跟线程都会遵循:守护xxx会等待主xxx运行完毕后被销毁

主进程:运行完毕指的是主进程代码运行完毕

主线程:运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕

注意:运行完毕并非终止运行

from threading import Thread
import time

def task1():
  print(‘123‘)
  time.sleep(10)
  print(‘123done‘)

def task2():
  print(‘456‘)
  time.sleep(1)
  print(‘456done‘)

if __name__ == ‘__main__‘:
  t1=Thread(target=task1)
  t2=Thread(target=task2)
  t1.daemon=True
  t1.start()
  t2.start()
  print(‘主‘)

 

7、GIL全局解释器锁

GIL本质是一把互斥锁,将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

from threading import Thread
n=100
  def task():
    print(‘is running‘)

if __name__ == ‘__main__‘:
  t1=Thread(target=task,)
  t2=Thread(target=task,)
  t3=Thread(target=task,)
  # t=Process(target=task,)
  t1.start()
  t2.start()
  t3.start()
  print(‘主‘)

多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行

解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行n=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码

结论:

1、如果是I/O密集型的,可以开启多线程

2、如果是要求计算性能,可以利用多核,开启进程

 

 

8、同步锁

1、线程抢的是CIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁lock,其他线程也可以抢到GIL,但如果发现lock仍然没有被释放则阻塞,即便是拿到执行权限GIL也要立刻交出来

2、jion是等待所有,即整体串行,而锁只是锁住修改共享数据的部分,即部分串行,要想保住数据安全性的根本原来在于让并发变成串行,jion与互斥锁都可以实现,互斥锁的部分串行效率更高

以上是关于并发编程之多线程的主要内容,如果未能解决你的问题,请参考以下文章

并发编程路线

并发编程之多线程

python并发编程之多线程

python并发编程之多线程

Python并发编程系列之多线程

python并发编程之多线程