Python -- 线程

目录

1.线程安全问题

1.1 线程访问全局变量

1.2 线程的安全问题

2.线程锁

2.1 同步

2.2 互斥锁

2.3 使用互斥锁解决卖票问题

2.4 上锁过程:

3.线程间通信

3.1 线程间通过

3.2 Queue的原理

4.多线程版聊天


1.线程安全问题

1.1 线程访问全局变量

 import threading
 g_num = 0
 def 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()

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

1.2 线程的安全问题

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

2.线程锁

2.1 同步

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

2.2 互斥锁

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

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

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

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

注意:

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

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

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

2.3 使用互斥锁解决卖票问题

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

2.4 上锁过程:

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

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

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

总结

锁的好处:

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

锁的坏处:

  • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。

  • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。

3.线程间通信

3.1 线程间通过

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

3.2 Queue的原理

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

 import threading
 import time
 from 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()

4.多线程版聊天

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值