Python线程

目录

一、线程的概念

二、进程和线程之间的关系

三、使用threading模块创建线程

1.import threading

2.传递参数:

3.join()方法

4.setDeamon()

四、实例方法

五、threading模块提供的方法

六、使用继承的方式开启线程

1.继承threading.Thread

复写父类的run()方法

2.传递参数

七、多线程共享全局变量

1.多线程共享全局变量

2.共享全局变量的问题

3.解决共享全局变量的方法:互斥锁

(1)同步,异步

(2)互斥锁

八、死锁

1.死锁产生的原因

2.注意

九、线程队列

十、生产者与消费者模型

1.PV操作

2.用python的队列实现

十一、GIL全局解释锁     global interpreter lock

1.并行,并发

(1)并行

(2)并发

2.GIL

小彩蛋 :如何下载包?

(1)pycharm


一、线程的概念

进程是资源的拥有者

进程是资源分配和调度的最小单位

线程是CPU调度的最小单位(程序真正执行的时候运行的是线程)

每个进程至少有一个线程


二、进程和线程之间的关系


三、使用threading模块创建线程

1.import threading

threading.Thread(target=函数名,arge=(参数1,参数2......))

target指向执行的对象
arge参数,保存在元组中

等子线程结束后,主线程结束,程序结束了


2.传递参数:


3.join()方法

当前线程执行完后,其他线程才会继续执行

import time
import threading


def sing():
    for i in range(3):
        print('正在唱歌...', i)
        time.sleep(1)


def dance():
    for i in range(3):
        print('正在跳舞...', i)
        time.sleep(1)


def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t1.join()  # 效果:t1执行完后,t2和主线程同时执行
    t2.start()
    # t1.join()  # 效果:t1,t2子进程执行,当t1,t2结束了,主线程才执行
    # t2.join()


if __name__ == '__main__':
    main()
    print('程序结束了...')

4.setDeamon()

将当前线程设置成守护线程,守护主线程

当主线程结束后,守护线程也结束了,不管是否执行完

import time
import threading


def sing():
    for i in range(3):
        print('正在唱歌...', i)
        time.sleep(1)


def dance():
    for i in range(3):
        print('正在跳舞...', i)
        time.sleep(1)


def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    # 把t1,t2都设置成守护线程,主线程一死,t1,t2都要死,不管他们有没有执行完
    # r如果只把其中一个设置成守护线程,一个会把另一个给救了,程序正常执行
    t1.setDaemon(True)
    t2.setDaemon(True)
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()
    print('程序结束了...')
# 正在唱歌... 0
# 正在跳舞... 0
# 程序结束了...

四、实例方法

getName()获取线程的名字
setName()设置线程的名字
is_alive()判断线程是否活着
'''
实例方法:
getName()
setName()
is_alive():判断线程是否活着

'''
import time
import threading


def sing():  # t1子线程执行的开始
    for i in range(3):
        print('正在唱歌...', i)
        time.sleep(1)


def dance():  # t2子线程执行的开始
    for i in range(3):
        print('正在跳舞...', i)
        time.sleep(1)


def main():
    t1 = threading.Thread(target=sing)  # 创建t1子线程,执行sing函数
    t2 = threading.Thread(target=dance)
    print(t1.is_alive())  # False  用is_alive()  下划线命名法,比较规范
    print(t2.is_alive())  # False
    t1.start()  # 开启t1子线程
    print(t1.is_alive())  # True
    t2.start()
    # 给线程设置名称
    t1.setName('张飞')
    t2.setName('关羽')
    # 获取线程的名称
    print(t1.getName())
    print(t2.getName())


if __name__ == '__main__':
    main()  # 主线程执行的开始
    print('程序结束了...')
# False
# False
# 正在唱歌... 0
# True
# 正在跳舞... 0
# 张飞
# 关羽
# 程序结束了...
# 正在跳舞... 1
# 正在唱歌... 1
# 正在跳舞... 2
# 正在唱歌... 2

五、threading模块提供的方法

current_thread()返回当前的线程对象

enumerate()

返回正在运行的线程,存放在一个列表中
active_count()返回现在活着的线程的数量
import time
import threading


def sing():
    for i in range(3):
        print('正在唱歌...', i)
        time.sleep(1)
    print(threading.current_thread())  # <Thread(Thread-2, started 6868)>


def dance():
    for i in range(3):
        print('正在跳舞...', 3)
        time.sleep(1)
    print(threading.current_thread())  # <Thread(Thread-1, started 4704)>


def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()
    print(threading.current_thread())  # <_MainThread(MainThread, started 6484)>
    print(threading.enumerate())
    # [<_MainThread(MainThread, started 6484)>, <Thread(Thread-1, started 4704)>, <Thread(Thread-2, started 6868)>]
    print(threading.active_count())  # 3


if __name__ == '__main__':
    main()
    print('程序结束了...')

六、使用继承的方式开启线程

1.继承threading.Thread

复写父类的run()方法

2.传递参数


七、多线程共享全局变量

1.多线程共享全局变量

2.共享全局变量的问题

两个线程的操作会不会出现问题
全写有问题
一个读,一个写可能会有问题
全读没问题

只要涉及到写,就可能会有问题

'''
共享变量的问题:
当传进去100的时候,没问题,100  200
当传进去1000000的时候,出现问题,  并且每次结果都不一样  数字变少了
写100的时候,消耗的代码相当少
一个读一个写
两个都写
多个线程同时操作全局变量,就会有问题

两个都读的话就没问题
只要涉及到写的话,就有可能有问题
想要解决这个问题的话:

'''
# 一个读一个写
import threading

g_num = 0


def test1(num):
    global g_num
    for i in range(num):
        g_num += 1
    print('test1--->', g_num)


def test2(num):
    global g_num
    for i in range(num):
        g_num += 1
    print('test2-->', g_num)


if __name__ == '__main__':
    t1 = threading.Thread(target=test1, args=(1000000,))
    t2 = threading.Thread(target=test2, args=(1000000,))
    t1.start()
    t2.start()

'''
1.先获取g_num的值
2.g_num+1
3.将g_num的值保存在g_num中

t1获取到cpu,g_num=1
此时,t2获取到cpu,
'''
import threading

g_num = 0


def test1(num):
    global g_num
    for i in range(num):
        g_num += 1
    print('test1--->', g_num)


def test2(num):
    print('test2-->', g_num)


if __name__ == '__main__':
    t1 = threading.Thread(target=test1, args=(1000000,))
    t2 = threading.Thread(target=test2, args=(1000000,))
    t1.start()
    t2.start()
# test2--> 235981
# test1---> 1000000

3.解决共享全局变量的方法:互斥锁

(1)同步,异步

同步:合作

同步是指协同、协助、互相配合。

A强依赖B(对方),A必须等到B的回复,才能做出下一步响应。即A的操作(行程)是顺序执行的,中间少了哪一步都不可以,或者说中间哪一步出错都不可以。

异步:

异步则相反,A并不强依赖B,A对B响应的时间也不敏感,无论B返回还是不返回,A都能继续运行;B响应并返回了,A就继续做之前的事情,B没有响应,A就做其他的事情。也就是说A不存在等待对方的概念。


(2)互斥锁

当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。


八、死锁

1.死锁产生的原因

在多个线程共享资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁现象。

2.注意

如果锁之间相互嵌套,就有可能出现死锁。因此尽量不要出现锁之间的嵌套。


九、线程队列

put()往队列里面放数据
put_nowait()往队列里面放数据,当队列满的时候,报错
get()从队列中取数据
get_nowait()从队列中取数据,当队列空的时候,报错
qsize()返回队列的长度
empty()判断队列是否为空
full()判断队列时候满
task_done()发出(队列为空)信号
join()接收(队列为空)信号

task_done():任务结束  发出信号

join():接收信号

import queue

q = queue.Queue()
q.put('hahaha...')
print(q.get())
q.task_done()
q.join()
print('---->>>')

十、生产者与消费者模型

1.PV操作

semaphore mutex=1;#临界区的互斥信号量
semaphore empty=n;#空闲缓存区
semaphore full=0;#缓存区初始化为空
producer()#生产者进程
{
    while(1)
    {
        produce an item in nextp;#生产数量
        P(empty);#获取空缓冲区
        p(mutex);#进入临界区
        add nextp to buffer;#将数据放入缓冲区
        v(mutex)#离开缓冲区
        v(full)#满缓冲区数加1
    }
}
consumer()#消费者进程
{
    while(1)
    {
        p(full);#获取满缓冲区单元
        p(mutex);#进入临界区
        remove an item from buffer;#从缓冲区中取出数据
        v(mutex);#离开缓冲区
        v(empty);#空缓冲区数加1
        consume the item;#消费数据
    }
}


2.用python的队列实现

import queue, threading

q = queue.Queue()
lock = threading.Lock()


def producer(name):
    count = 1
    while count <= 100:
        q.join()
        lock.acquire()
        q.put(count)
        print('{}正在做第{}碗面条'.format(name, count))
        count+=1
        lock.release()


def consumer(name):
    count = 1
    while count <= 100:
        q.get()
        lock.acquire()
        print('{}正在做第{}碗面条'.format(name, count))
        count+=1
        lock.release()
        q.task_done()



def main():
    t1 = threading.Thread(target=producer, args=('海参',))
    t2 = threading.Thread(target=consumer, args=('小宝',))
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

 


十一、GIL全局解释锁     global interpreter lock

1.并行,并发

(1)并行

多个CPU同时执行多个任务,就好像有两个程序,这两个程序是真的在两个不同的CPU内同时被执行。

(2)并发

CPU交替处理多个任务,还是有两个程序,但是只有一个CPU,会交替处理这两个程序,而不是同时执行,只不过因为CPU执行的速度过快,而会使得人们感到是在“同时”执行,执行的先后取决于各个程序对于时间片资源的争夺.

并行和并发同属于多任务,目的是要提高CPU的使用效率。这里需要注意的是,一个CPU永远不可能实现并行,即一个CPU不能同时运行多个程序。

2.GIL

Guido van Rossum(吉多·范罗苏姆)创建python时就只考虑到单核cpu,解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁, 于是有了GIL这把超级大锁。因为cpython解析只允许拥有GIL全局解析器锁才能运行程序,这样就保证了保证同一个时刻只允许一个线程可以使用cpu。也就是说多线程并不是真正意义上的同时执行。





小彩蛋 :如何下载包?

(1)pycharm

setting-->Project-->Project Interpreter-->  +   ----->在搜索框中输入自己需要的包-->Install Package

import pygame

pygame.init()
pygame.display.set_mode((320, 568))
while True:
    pass
pygame.quit()
  • 运行结果:

  • 控制台的信息:


  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值