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

相关推荐
湫ccc7 分钟前
《Python基础》之基本数据类型
开发语言·python
Natural_yz12 分钟前
大数据学习17之Spark-Core
大数据·学习·spark
qq_1728055919 分钟前
RUST学习教程-安装教程
开发语言·学习·rust·安装
一只小小汤圆34 分钟前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
醉陌离36 分钟前
渗透测试笔记——shodan(4)
笔记
虾球xz42 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
LateBloomer7771 小时前
FreeRTOS——信号量
笔记·stm32·学习·freertos
legend_jz1 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py1 小时前
【Linux】-学习笔记04
linux·笔记·学习
drebander1 小时前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list