并发编程(二) 线程

1.线程基本概念

什么是线程

  进程和线程都是虚拟的单位,都是用来帮助我们形象的描述某种事物

  进程是资源分配的最小单位,线程是cpu调度的最小单位,每一个进程中至少有一个线程,进程只是在线程运行过程中提供代码运行所需的资源

  将内存比作工厂,那么进程就相当于工厂的车间,而线程就相当于车间内的流水线

为什么要有线程

  进程

    一个进程只能在一个时间干一件事,如果想干多件事,进程就无能为力了

    开一个进程需要开辟一个新的内存空间,将代码丢进去执行,这是一件十分耗费资源的事

  线程

    一个进程中可以开多个线程,并且线程和线程之间的数据也是共享的

    开启线程的资源耗费是远远小于开启一个进程的

进程和线程的区别:

  1.地址空间的区别和资源之间的共享

  2.通信:进程间通信要通过队列来实现,而线程之间可以直接读写所处进程中的数据来通信

  3.调度和切换:线程的调度切换比进程快的多

  4.在多线程操作系统中,进程不是一个可执行的实体

2.创建线程的两种方式

  和创建进程类似,创建线程也有两种方式

 1 from threading import Thread
 2 import time
 3 
 4 def task(name):
 5     print('%s开始'%name)
 6     time.sleep(1)
 7     print('%s结束'%name)
 8 
 9 # 线程的代码块不需要写在__main__内,但是我们还是习惯性的写在里边
10 t = Thread(target=task,args=('sxc',))
11 t1 = Thread(target=task,args=('zzj',))
12 t.start()
13 t1.start()
14 print('')
 1 from threading import Thread
 2 import time
 3 
 4 class MyThread(Thread):  # 新建一个类继承父类Thread的方法
 5     def __init__(self,name):
 6         super().__init__()
 7         self.name = name
 8 
 9     def run(self):
10         print('%s开始'%self.name)
11         time.sleep(1)
12         print('%s结束'%self.name)
13 
14 if __name__ == '__main__':
15     t1 = MyThread('sxc')  # 类名加括号实例化对象
16     t2 = MyThread('zzj')
17     t1.start()
18     t2.start()
19     print('')
第二种方式

注意:创建线程时,我们可以不将执行的代码块放在__main__方法内,但是习惯还是放在里边 

3.线程对象及其他方法

current_thread().name:查看当前的线程号
os.getpid:查看当前的进程号
t.join():主线程等待子线程运行结束
active_count():查看当前活跃的进程数
 1 from threading import Thread,current_thread,active_count
 2 import os
 3 import time
 4 
 5 def task(name):
 6     print('%s开始'%name)
 7     print('子current_thread:',current_thread().name)  # 查看当前线程
 8     print('子os.getpid',os.getpid())  # 查看进程号
 9     time.sleep(1)
10     print('%s结束'%name)
11 
12 if __name__ == '__main__':
13     t = Thread(target=task,args=('sxc',))
14     t.start()
15     print('当前活跃的线程',active_count())  # 查看当前活跃的进程数
16     t.join()  # 主线程等待子线程结束才能执行下方代码
17     print('主current_thread:', current_thread().name)  # 查看当前线程
18     print('主os.getpid', os.getpid())  # 查看进程号
19     print('')
20     print('当前活跃的线程', active_count())

多线程也能实现TCP并发

 1 # 多线程实现并发
 2 # 服务端
 3 import socket
 4 from threading import Thread
 5 
 6 server = socket.socket()
 7 server.bind(('127.0.0.1',8080))
 8 server.listen()
 9 
10 def task(conn):
11     while True:
12         try:
13             data = conn.recv(1024)
14             if len(data) == 0:break
15             print(data.decode('utf-8'))
16             conn.send(data.upper())
17         except ConnectionResetError as e:
18             print(e)
19             break
20     conn.close()
21 
22 if __name__ == '__main__':
23 
24     while True:
25         conn, addr = server.accept()
26         t = Thread(target=task,args=(conn,))
27         t.start()
多线程实现并发服务端
 1 # 多线程实现并发客户端
 2 import socket
 3 from threading import Thread,current_thread
 4 
 5 client = socket.socket()
 6 client.connect(('127.0.0.1',8080))
 7 
 8 def task(client,i):
 9     while True:
10         msg = '线程%s已开启%s'%(current_thread().name,i)
11         client.send(msg.encode('utf-8'))
12         data = client.recv(1024)
13         print(data.decode('utf-8'))
14 
15 if __name__ == '__main__':
16     for i in range(10):
17         t = Thread(target=task,args=(client,i))
18         t.start()
多线程实现并发客户端

4.守护线程

  无论输进程还是线程,只要设置了守护之后,守护的进程或线程等待主运行完毕后会被销毁

 1 from threading import Thread
 2 import time
 3 
 4 def task(name):
 5     print('%s开始'%name)
 6     time.sleep(1)
 7     print('%s结束'%name)
 8 
 9 if __name__ == '__main__':
10     t = Thread(target=task,args=('sxc',))
11     t.daemon = True  # 将子进程设置为守护线程,必须在子线程开始之前设置,否则会报错
12     t.start()
13     print('')

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

 1 from threading import Thread
 2 import time
 3 
 4 def task(name,i):
 5     print('%s开始'%name)
 6     time.sleep(i)
 7     print('%s结束'%name)
 8 
 9 if __name__ == '__main__':
10     t = Thread(target=task,args=('sxc',1))
11     t1 = Thread(target=task,args=('zzj',2))
12     t.daemon = True  # 将子进程设置为守护线程,必须在子线程开始之前设置,否则会报错
13     t.start()
14     t1.start()
15     print('')  # 只有所有非守护进程结束才算真正的运行结束
守护进程的小例子

判断上述例子的打印结果

5.线程间通信,数据共享

  进程间数据是不能共享的,因此需要引入队列来进行通信,而线程的数据输共享的,可以直接通信

 1 from threading import Thread
 2 
 3 age = 18
 4 
 5 def task():
 6     global age
 7     age = 20
 8     print('子:',age)
 9 
10 if __name__ == '__main__':
11     print('生成子之前的主:',age)
12     t = Thread(target=task)
13     t.start()
14     t.join()
15     print('生成子之后的主:',age)

6.互斥锁

   在多个线程修改共享的数据时,数据安全性和可靠性不能保证,需要进行加锁处理

 1 from threading import Thread,Lock
 2 import time
 3 n = 100
 4 
 5 def task(mutex):
 6     mutex.acquire()
 7     global n
 8     temp = n
 9     time.sleep(0.1)
10     n = temp - 1
11     print(n)
12     mutex.release()
13     # print(n)
14 
15 if __name__ == '__main__':
16     mutex = Lock()  # 实例化锁对象
17     t_list = []
18     for i in range(100):
19         t = Thread(target=task,args=(mutex,))  # 把锁传入
20         t.start()
21         t_list.append(t)
22     for t in t_list:
23         t.join()
24     print(n)

注意:这个锁我们只需要加在共享的数据部分就行了,即只有加锁的部分是串行,这样能在保证数据安全的前提下,最大化效率.

31

原文地址:https://www.cnblogs.com/sxchen/p/11342053.html