多任务编程,线程,多线程,互斥锁

回顾:
进程时最小的资源分配单位

1.线程
线程时最小的执行单位

线程也是实现多任务的一种方式
一个程序在执行时会对应一个主进程,主进程中会有一个主线程

通过主线程手动产生的线程称为子线程

2.多线程的使用
1.导入模块 import threading
2.实现多任务的功能函数 def
3.创建线程 线程名 = threading.Thread(target=定义的函数名)
4.启动线程 子线程名.start

3.线程执行带有参数的任务
args 以元组的方式给执行任务传参
kwargs 以字典的方式给执行任务传参
‘’’
线程任务参数
‘’’

1. 导模块
import threading
import time

2. 实现任务函数
def task1(count):
for i in range(count):
print('Hello – ', i+1)
time.sleep(0.5)

def task2(content, count):
for i in range(count):
print(content, ’ – ', i+1)
time.sleep(0.5)

创建线程
if name == ‘main’:
# # 方式一 通过 arsg 传入元组来为多任务传参
# t1 = threading.Thread(target=task1,args=(5,))
# t2 = threading.Thread(target=task2,args=(‘Python’,5))

# 方式二 通过 kwarsg 传入字典来为多任务传参
t1 = threading.Thread(target=task1, kwargs={'count': 5})
t2 = threading.Thread(target=task2, kwargs={'count': 5, 'content': 'World' })
t1.start()
t2.start()

4.线程之间执行是无序的

线程之间的执行时无序的,它是由cpu调度决定的,cpu调度那个线程,
哪个线程就先执行,没有调度的线程不能执行

进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,
哪个进程就先执行,没有调度的进程不能执行

5.主线程会等待所有子线程执行完成以后在结束

主线程会等待所有的子线程执行结束在结束,所以这样我们可以设置守护主线程
守护主线程就是主线程退出子线程销毁不再执行
设置守护主线程有两种方式:
1.threading.Thread(target=show_info,daemon=True)
2.线程对象.SetDaemon(True)

6.线程之间共享全局变量
线程之间为什么能共享全局变量呢
线程并不像进程那样创建一个进程就复制一次代码
因为线程都是在一个进程运行的定义的全局变量也是在这个进程中的,
所以是可以共享全局变量的

7.线程之间共享全局变量数据错误问题
线程间的共享全局变量会出现资源竞争问题

解决方案:
 1.线程等待(join)
 2.互斥锁

线程等待:线程名.join()
	.join()的放置位置是两个子线程的执行之间
	
	线程1.start()
	线程1.join()    --> 一个任务执行完成之后才会执行另外一个任务,这样同时
		             只有一个任务在执行,是属于同步执行(同步执行:执行完一个任务才会执行下一个任务)
	线程2.start()

8.互斥锁

对共享数据进行锁定,保证同一时刻只能由一个线程去操作

锁名称 = threading.Lock()

上锁:
锁名称.acquire()

释放锁:
锁名称.release()

acquire 和 release 方法直接的代码同一时刻只能有一个线程去操作
如果在调用 acquire 方法的时候,其他线程已经使用了这个互斥锁,那么此时 acquire 方法会堵塞
直到这个互斥锁释放后才能再次上锁

 # 提示:加上互斥锁,那个线程抢到这个锁我们决定不了,那线程抢到锁那个线程先执行,
              没有抢到的线程需要等待
# 加上互斥锁多任务瞬间变成单任务,性能会下降,也就是说同一时刻只能有一个线程去执行
    但是互斥锁能够保证多个线程访问共享数据不会出现数据错误问题


1.互斥锁的作用就是保证同一时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题
2.使用互斥锁的好处确保某段关键代码只能由一个线程从头到尾完整地去执行
3.使用互斥锁会影响代码的执行效率,多任务改成了单任务执行
4.互斥锁如果没有使用好容易出现死锁的情况

9.死锁
死锁:一直等待对方释放锁的情景就是死锁
1.使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁。
2.死锁一旦产生就会造成应用程序的停止响应,应用程序无法再继续往下执行了。

 开发时要避免出现思死锁
出现死锁时要合理安排上锁和释放锁的位置

10.进程和线程的对比
关系对比:
1.线程依附在进程里面,没有进程就没有线程
2.一个进程默认提供一条线程,进程可以创建多个线程

区别对比:
1.进程之间不共享全局变量
2.线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁或者线程同步
3.创建进程的资源开销要比创建线程的资源开销要大
4.进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
5.线程不能够独立执行,必须依存在进程中
6.多进程开发比单进程多线程开发稳定性要强

优缺点对比:
进程优缺点:
优点:可以用多核
缺点:资源开销大

线程优缺点:
优点:资源开销小
缺点:不能使用多核

1.进程和线程都是完成多任务的一种方式
2.多进程要比多线程消耗的资源多,但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉不会影响其它进程。
3.多进程可以使用cpu的多核运行,多线程可以共享全局变量。
4.线程不能单独执行必须依附在进程里面

1.线程使用

'''
线程使用
'''

# 1. 导入模块
import threading
import time

# 实现多任务的功能函数

def task1():
    # 获取当前线程对象
    t = threading.current_thread()
    print('TaskA :', t.name)

    for i in range(5):
        print('Task A  ',i+1)
        time.sleep(0.5)



def task2():
    # 获取当前线程对象
    t = threading.current_thread()
    print('TaskB :', t.name)
    for i in range(5):
        print('Task B  ',i+1)
        time.sleep(0.5)


# 创建线程
if __name__ == '__main__':
    # 获取当前线程对象
    t = threading.current_thread()
    print('Main :', t.name)

    t1 = threading.Thread(target=task1,name='T1')
    t2 = threading.Thread(target=task2,name='T2')

    print(t1)
    print(t2)

    # 创建好线程后,线程并不会执行
    # 必须启动线程
    t1.start()
    t2.start()

    print(t1)
    print(t2)
    time.sleep(0.5)

    print('Main Thread')

2.线程参数

'''
线程使用
'''

# 1. 导入模块
import threading
import time

# 实现多任务的功能函数

def task1():
    for i in range(5):
        print('Task A  ',i+1)
        time.sleep(0.5)



def task2():
    for i in range(5):
        print('Task B  ',i+1)
        time.sleep(0.5)


# 创建线程
if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)

    # 创建好线程后,线程并不会执行
    # 必须启动线程
    t1.start()
    t2.start()

    time.sleep(0.5)

    print('Main Thread')

3.线程任务参数

'''
线程任务参数
'''

# 1. 导模块
import threading
import time

# 2. 实现任务函数
def task1(count):
    for i in range(count):
        print('Hello -- ', i+1)
        time.sleep(0.5)


def task2(content, count):
    for i in range(count):
        print(content, ' -- ', i+1)
        time.sleep(0.5)


# 创建线程
if __name__ == '__main__':
    # # 方式一 通过 arsg 传入元组来为多任务传参
    # t1 = threading.Thread(target=task1,args=(5,))
    # t2 = threading.Thread(target=task2,args=('Python',5))

    # 方式二 通过 kwarsg 传入字典来为多任务传参
    t1 = threading.Thread(target=task1, kwargs={'count': 5})
    t2 = threading.Thread(target=task2, kwargs={'count': 5, 'content': 'World' })
    t1.start()
    t2.start()

4.线程执行时是无序的

'''
线程执行时是无序的
'''

import threading
import time

def task():
    # 打印线程名
    t = threading.current_thread()

    for i in range(10):
        print(t.name)
        time.sleep(0.5)


if __name__ == '__main__':
    for i in range(5):
        t = threading.Thread(target=task)
        t.start()

5.设置守护线程

'''
设置守护线程
'''

import threading
import time

def task():
    # 打印线程名
    t = threading.current_thread()

    for i in range(5):
        print(t.name)
        time.sleep(0.5)


if __name__ == '__main__':
    # 无守护线程状态
    # t1 = threading.Thread(target=task)
    # t2 = threading.Thread(target=task)


    # # 设置守护线程的方式一
    # t1 = threading.Thread(target=task, daemon=True)
    # t2 = threading.Thread(target=task, daemon=True)

    # 设置守护线程的方式二
    t1 = threading.Thread(target=task)
    t2 = threading.Thread(target=task)

    t1.setDaemon(True)
    t2.setDaemon(True)


    t1.start()
    t2.start()
    print('Main Over')

6.线程间可以共享全局变量

'''
线程间可以共享全局变量
'''

import threading
import time


c_list = []

def add_data():
    for i in range(5):
        c_list.append(i)
        print(c_list)
        time.sleep(0.5)


def read_data():
    print("read: ", c_list)


if __name__ == '__main__':
    add_t = threading.Thread(target=add_data)
    read_t = threading.Thread(target=read_data)

    add_t.start()
    read_t.start()

    # 运行时,由于线程的执行顺序无法控制,有可能先去执行读取线程,那么这时读取出来的数据 是空
    # 这是正常的

7.线程间共享全局变量时的资源竞争问题

'''
线程间共享全局变量时的资源竞争问题
'''

import threading
import time

# 全局变量,用来保存最后的累加和的变量
sum = 0

def add_num():
    global sum
    for i in range(1000000):
        sum += 1

    t = threading.current_thread()
    print(f'{t.name} 的计算结果是{sum}')


if __name__ == '__main__':
    t1 = threading.Thread(target=add_num)
    t2 = threading.Thread(target=add_num)

    t1.start()
    t2.start()


    time.sleep(3)
    print('Main:',sum)

8.线程间共享全局变量时的资源竞争问题

'''
线程间共享全局变量时的资源竞争问题
'''

import threading
import time

# 全局变量,用来保存最后的累加和的变量
sum = 0

def add_num():
    global sum
    for i in range(1000000):
        sum += 1

    t = threading.current_thread()
    print(f'{t.name} 的计算结果是{sum}')


if __name__ == '__main__':
    t1 = threading.Thread(target=add_num)
    t2 = threading.Thread(target=add_num)

    t1.start()

    # 在这里使用 join () 方法来设置线程同
    # join() 方法的做是将调用该方法的线程加入到当前线程之前
    # 当前线程会等待加入的线程执行完成后,才会继续执行
    t1.join()

    t2.start()


    time.sleep(3)
    print('Main:',sum)

9.使用线程互斥锁解决线程间共享全局变量时的资源竞争问题

'''
使用线程互斥锁解决线程间共享全局变量时的资源竞争问题
'''

import threading
import time


# 实例一个锁对象
metax_lock = threading.Lock()
# 为了能让锁起作用
# 一般会将这个锁加到对共享数据资源进行操作的代码上

# 全局变量,用来保存最后的累加和的变量
sum = 0

def add_num():
    t = threading.current_thread()

    global sum
    # 加锁
    metax_lock.acquire()

    for i in range(1000000):
        sum += 1
        print(t.name, ' -- ', sum)

    # 解锁
    metax_lock.release()

    print(f'{t.name} 的计算结果是{sum}')


if __name__ == '__main__':
    t1 = threading.Thread(target=add_num)
    t2 = threading.Thread(target=add_num)

    t1.start()
    t2.start()


    time.sleep(3)
    print('Main:',sum)

10.使用线程互斥锁同步解决线程间共享全局变量时的资源竞争问题

'''
使用线程互斥锁同步解决线程间共享全局变量时的资源竞争问题
'''

import threading
import time


# 实例一个锁对象
metax_lock = threading.Lock()
# 为了能让锁起作用
# 一般会将这个锁加到对共享数据资源进行操作的代码上

# 全局变量,用来保存最后的累加和的变量
sum = 0

def add_num():
    t = threading.current_thread()

    global sum


    for i in range(1000000):
        # 加锁
        metax_lock.acquire()
        sum += 1
        # print(t.name, ' -- ', sum)
        # 解锁
        metax_lock.release()

    print(f'{t.name} 的计算结果是{sum}')


if __name__ == '__main__':
    t1 = threading.Thread(target=add_num)
    t2 = threading.Thread(target=add_num)

    t1.start()
    t2.start()


    time.sleep(3)
    print('Main:',sum)

11.死锁产生的情况1

'''
死锁产生的情况1
'''

import threading

c_list = [1,2,3]

# 创建一个锁
lock = threading.Lock()


def get_value(index):

    # 加锁
    lock.acquire()

    t = threading.current_thread()

    # 加个判断用来解决下标越界问题
    if index >= len(c_list):
        print(f'{index} 下太大,导致下标越界')
        # 因为下标的问题,应该在结束该线程之前,将这个锁给释放掉
        lock.release()
        return

    print(t.name ,'取得第', index, '个值,值为',c_list[index])

    # 解锁
    lock.release()

if __name__ == '__main__':
    for i in range(5):
        t = threading.Thread(target=get_value, args=(i,))
        t.start()

11.哲学家死锁问题(2)

'''
哲学家死锁问题
'''

import threading

lock_a = threading.Lock()
lock_b = threading.Lock()

def person_a():
    for i in range(100):
        lock_a.acquire()
        print('A 加第一个锁,抢到第一根筷子')
        lock_b.acquire()
        print('A 加第二个锁,抢到第二根筷子,吃一口饭')
        lock_b.release()
        print('A 释放第二个锁,放下第二根筷子')
        lock_a.release()
        print('A 释放第一个锁,放下第一根筷子')



def person_b():
    for i in range(100):
        lock_b.acquire()
        print('B 加第一个锁,抢到第一根筷子')
        lock_a.acquire()
        print('B 加第二个锁,抢到第二根筷子,吃一口饭')
        lock_a.release()
        print('B 释放第二个锁,放下第二根筷子')
        lock_b.release()
        print('B 释放第一个锁,放下第一根筷子')

# def call_func():
#     return person_b
#
#
# if __name__ == '__main__':
#     pa = threading.Thread(target=call_func())

if __name__ == '__main__':
    pa = threading.Thread(target=person_a)
    pb = threading.Thread(target=person_b)
    pa.start()
    pb.start()

你可能感兴趣的:(系统学习python)