python线程教程,python线程学习笔记

线程访问全局变量

复制代码
import threading
复制代码
g_num = 0def test(n):
复制代码
`    `global g_num
复制代码
`    `for x in range(n):
复制代码
        g_num += x
复制代码
        g_num -= x
复制代码
    print(g_num)
复制代码
if __name__ == '__main__':
复制代码
    t1 = threading.Thread(target=test, args=(10,))
复制代码
    t2 = threading.Thread(target=test, args=(10,))
复制代码
    t1.start()
复制代码
    t2.start()

在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据。缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)。

线程的安全问题

复制代码
import threadingimport time
复制代码
复制代码
ticket = 20
复制代码
复制代码
def sell_ticket():
复制代码
`    `global ticket
复制代码
`    `while` `True:
复制代码
`        `if ticket > 0:
复制代码
            time.sleep(0.5)
复制代码
            ticket -= 1
复制代码
            print('{}卖了一张票,还剩{}'.format(threading.current_thread().name, ticket))
复制代码
`        `else:
复制代码
            print('{}票卖完了'.format(threading.current_thread().name))
复制代码
`            `break
复制代码
复制代码
for i in range(5):
复制代码
    t = threading.Thread(target=sell_ticket, name='thread-{}'.format(i + 1))
复制代码
    t.start()

同步

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。同步就是协同步调,按预定的先后次序进行运行。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

互斥锁

互斥锁为资源引入一个状态:锁定/非锁定

某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能更改;直到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

threading模块中定义了Lock类,可以方便的处理锁定:

复制代码
# 创建锁
复制代码
mutex = threading.Lock()# 锁定
复制代码
mutex.acquire()# 释放
复制代码
mutex.release()

注意:

  • 如果这个锁之前是没有上锁的,那么acquire不会堵塞

如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止。

和文件操作一样,Lock也可以使用with语句快速的实现打开和关闭操作。

使用互斥锁解决卖票问题

复制代码
import threadingimport time
复制代码
复制代码
ticket = 20
复制代码
lock = threading.Lock()
复制代码
复制代码
def sell_ticket():
复制代码
`    `global ticket
复制代码
`    `while` `True:
复制代码
        lock.acquire()
复制代码
`        `if ticket > 0:
复制代码
            time.sleep(0.5)
复制代码
            ticket -= 1
复制代码
            lock.release()
复制代码
            print('{}卖了一张票,还剩{}'.format(threading.current_thread().name, ticket))
复制代码
`        `else:
复制代码
            print('{}票卖完了'.format(threading.current_thread().name))
复制代码
            lock.release()
复制代码
`            `break
复制代码
复制代码
for i in range(5):
复制代码
    t = threading.Thread(target=sell_ticket, name='thread-{}'.format(i + 1))
复制代码
    t.start()

上锁过程:

当一个线程调用锁的acquire()方法获得锁时,锁就进入"locked"状态。

每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为"blocked"状态,称为"阻塞",直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入"unlocked"状态。

线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

总结

锁的好处:

  • 确保了某段关键代码只能由一个线程从头到尾完整地执行

锁的坏处:

  • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。
  • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。

线程间通信

线程之间有时需要通信,操作系统提供了很多机制来实现进程间的通信,其中我们使用最多的是队列Queue.

Queue的原理

Queue是一个先进先出(First In First Out)的队列,主进程中创建一个Queue对象,并作为参数传入子进程,两者之间通过put( )放入数据,通过get( )取出数据,执行了get( )函数之后队列中的数据会被同时删除,可以使用multiprocessing模块的Queue实现多进程之间的数据传递。

复制代码
import threadingimport timefrom queue import Queue
复制代码
def producer(queue):
复制代码
`    `for i in range(100):
复制代码
        print('{}存入了{}'.format(threading.current_thread().name, i))
复制代码
        queue.put(i)
复制代码
        time.sleep(0.1)
复制代码
`    `return
复制代码
def consumer(queue):
复制代码
`    `for x in range(100):
复制代码
        value = queue.get()
复制代码
        print('{}取到了{}'.format(threading.current_thread().name, value))
复制代码
        time.sleep(0.1)
复制代码
`        `if` `not value:
复制代码
`            `return
复制代码
if __name__ == '__main__':
复制代码
    queue = Queue()
复制代码
    t1 = threading.Thread(target=producer, args=(queue,))
复制代码
    t2 = threading.Thread(target=consumer, args=(queue,))
复制代码
    t3 = threading.Thread(target=consumer, args=(queue,))
复制代码
    t4 = threading.Thread(target=consumer, args=(queue,))
复制代码
    t6 = threading.Thread(target=consumer, args=(queue,))
复制代码
    t1.start()
复制代码
    t2.start()
复制代码
    t3.start()
复制代码
    t4.start()
复制代码
    t6.start()

多线程版聊天

import socket

import threading

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.bind(('0.0.0.0', 8080))

def send_msg():

ip = input('请输入您要聊天的ip:')

port = int(input('请输入对方的端口号:'))

while True:

msg = input('请输入聊天内容:')

s.sendto(msg.encode('utf-8'), (ip, port))

if msg == "bye":

ip = input('请输入您要聊天的ip:')

port = int(input('请输入对方的端口号:'))

def recv_msg():

while True:

content, addr = s.recvfrom(1024)

print('接收到了{}主机{}端口的消息:{}'.format(addr[0], addr[1], content.decode('utf-8')),file=open('history.txt', 'a', encoding='utf-8'))

send_thread = threading.Thread(target=send_msg)

recv_thread = threading.Thread(target=recv_msg)

send_thread.start()

recv_thread.start()

相关推荐
databook13 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar14 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户83562907805115 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_15 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机21 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i1 天前
drf初步梳理
python·django
每日AI新事件1 天前
python的异步函数
python