线程介绍
- 进程是分配资源的最小单位,线程是程序执行的最小单位
- 一个进程最少有一个线程,线程自己没有资源,共享进程的资源
多线程完成多任务
- 导入线程模块
import threading
- 创建线程对象
threading.Tread(target=[])
- 启动
对象.start()
import threading
import time
def coding():
for i in range(3):
print('coding')
time.sleep(0.2)
def music():
for i in range(3):
print('music')
time.sleep(0.2)
coding_thread = threading.Thread(target=coding)
music_thread = threading.Thread(target=music)
coding_thread.start()
music_thread.start()
codingmusic
codingmusic
music
coding
def coding(num):
for i in range(3):
print('coding')
time.sleep(0.2)
def music(count):
for i in range(3):
print('music')
time.sleep(0.2)
coding_thread = threading.Thread(target=coding, args=(3,))
music_thread = threading.Thread(target=music, kwargs={'count':4})
coding_thread.start()
music_thread.start()
codingmusic
codingmusic
coding
music
主线程和子线程的结束顺序
- 主线程会等待所有子线程之行结束后再结束
设置守护主线程的两种方法
work_thread = threading.Thread(target=work)
work_thread.start()
time.sleep(1)
print('main thread is over')
work_thread = threading.Thread(target=work)
work_thread.setDaemon(True)
work_thread.start()
time.sleep(1)
print('main thread is over')
线程之间的执行顺序
- 是无序的
通过获取线程信息来看一看是不是这样
def get_info():
time.sleep(0.5)
current_thread = threading.current_thread()
print(current_thread)
for i in range(10):
sub_thread = threading.Thread(target=get_info)
sub_thread.start()
<Thread(Thread-34, started 6253195264)>
<Thread(Thread-38, started 6320500736)><Thread(Thread-41, started 6370979840)><Thread(Thread-36, started 6286848000)><Thread(Thread-40, started 6354153472)><Thread(Thread-42, started 6387806208)><Thread(Thread-39, started 6337327104)>
<Thread(Thread-37, started 6303674368)>
<Thread(Thread-35, started 6270021632)><Thread(Thread-33, started 6236368896)>
线程之间共享全局变量
my_list = []
def write_data():
for i in range(5):
my_list.append(i)
print("add:", i)
print('write:', my_list)
def read_data():
print('read:', my_list)
if __name__ == '__main__':
# 创建写进程
write_thread = threading.Thread(target=write_data)
# 创建读进程
read_thread = threading.Thread(target=read_data)
write_thread.start()
time.sleep(1)
read_thread.start()
add: 0
add: 1
add: 2
add: 3
add: 4
write: [0, 1, 2, 3, 4]
read: [0, 1, 2, 3, 4]
线程之间共享全局变量数据出现错误
- 定义两个函数,循环100w次,每次全局变量+1
- 创建两个子线程执行对应函数,查看计算结果
g_num = 0
def func_1():
for i in range(1000000):
global g_num
g_num += 1
print("func_1", g_num)
def func_2():
for i in range(1000000):
global g_num
g_num += 1
print("func_2", g_num)
if __name__ == '__main__':
sum1_thread = threading.Thread(target=func_1)
sum2_thread = threading.Thread(target=func_2)
sum1_thread.start()
sum2_thread.start()
func_1func_2 1345500
1489972
在把改变的全局变量返回给全局变量之前,就被另一个线程拿走并进行加1的操作,就会导致出错
使用线程同步来解决
线程协同步调——线程同步
- 按照预定的先后次序进行运行
- 保证同一时刻只能有一个线程去操作全局变量
- 使用互斥锁
互斥锁
- 对共享的数据进行锁定,保证同一时刻只有一个线程去操作
- 需要竞争,只有抢到钥匙的人才能享用资源,否则就得等
g_num = 0
def func_1():
# 上锁
mutex.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("func_1", g_num)
# 解锁
mutex.release()
def func_2():
# 上锁
mutex.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("func_2", g_num)
# 解锁
mutex.release()
if __name__ == '__main__':
# 创建一把锁
mutex = threading.Lock()
sum1_thread = threading.Thread(target=func_1)
sum2_thread = threading.Thread(target=func_2)
sum1_thread.start()
sum2_thread.start()
func_1 1000000
func_2 2000000
死锁
- 一种现象,一直等待对方释放锁的情况
- 导致程序停止响应
g_num = 0
def func_1():
print("func_1....")
# 上锁
mutex.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("func_1", g_num)
# 解锁
# mutex.release()
def func_2():
print("func_2...")
# 上锁
mutex.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("func_2", g_num)
# 解锁
# mutex.release()
if __name__ == '__main__':
# 创建一把锁
mutex = threading.Lock()
sum1_thread = threading.Thread(target=func_1)
sum2_thread = threading.Thread(target=func_2)
sum1_thread.start()
sum2_thread.start()
func_1....
func_2...
func_1 1000000
进程和线程的对比
- 关系对比
- 线程依附于进程,没有进程就没有线程
- 一个进程至少有一个线程,进程可以创建多个线程
- 区别关系
- 进程不共享全局变量,线程共享
- 线程存在资源竞争,使用互斥锁解决
- 创建进程的资源开销更大
- 进程是资源分配基本单位,线程是执行的资本单位
- 线程不可独立执行,依存于进程
- 优缺点对比
- 进程
- 可以用多核(运行qq或微信这两个进程可以用cpu的多个核,并行)
- 资源大
- 线程
- 资源开销小
- 不能多核,需要排队