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()

相关推荐
奈斯。zs6 分钟前
yjs08——矩阵、数组的运算
人工智能·python·线性代数·矩阵·numpy
Melody20506 分钟前
tensorflow-dataset 内网下载 指定目录
人工智能·python·tensorflow
学步_技术7 分钟前
Python编码系列—Python抽象工厂模式:构建复杂对象家族的蓝图
开发语言·python·抽象工厂模式
青椒大仙KI1135 分钟前
24/9/19 算法笔记 kaggle BankChurn数据分类
笔记·算法·分类
Narutolxy41 分钟前
Python 单元测试:深入理解与实战应用20240919
python·单元测试·log4j
Amo Xiang1 小时前
2024 Python3.10 系统入门+进阶(十五):文件及目录操作
开发语言·python
liangbm31 小时前
数学建模笔记——动态规划
笔记·python·算法·数学建模·动态规划·背包问题·优化问题
潮汐退涨月冷风霜1 小时前
机器学习之非监督学习(四)K-means 聚类算法
学习·算法·机器学习
GoppViper1 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
B站计算机毕业设计超人1 小时前
计算机毕业设计Python+Flask微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI
爬虫·python·深度学习·算法·机器学习·自然语言处理·数据可视化