Python高级 多任务 线程

Python高级 多任务 线程

一、 多任务引入

1. 多任务
	a> 多任务
	   概念:  多个任务同时执行,就是多任务
	     windows软件、程序: 多任务的操作系统程序
	     自己编写的python程序: 
	        1. 能同时执行多个任务: 多任务程序
	        1. 不能同时执行多个任务: 单任务程序

	   作用: 
	      1. 提高了多任务的执行效率
	      2. 更充分利用了cpu
	   
	b> 并行与并发 : 表现方式 
	  并行:
	  	当任务数小于或者等于cpu核数时,每一个任务都有对应的cpu来处理执行,即任务真的是一起执行的
	  并发: 
	  	指的是任务数多于cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)




2. 模拟在线听歌程序

   - 下载音乐任务函数

   - 播放音乐任务函数

import time




def download_music():

    """模拟下载音乐,需要6秒"""

    for i in range(6):

        time.sleep(1)  # 休眠1秒,模拟耗时的操作1秒

        print("--正在下载音乐...--", i)



def play_music():

    """模拟播放音乐,需要6秒"""

    for i in range(6):

        time.sleep(1)  # 休眠1秒,模拟耗时的操作1秒

        print("--正在播放音乐...--", i)



if name == 'main':

    # 1. 下载音乐任务

    download_music()



    # 2. 播放音乐任务
    play_music()

二、多线程实现多任务(重点)

1. 对线程的理解(难点)
	1.一个程序运行起来至少有一个进程,一个进程至少有一个线程
	2.处理器cpu分配给线程,即cpu真正运行的是线程中的代码
	3.分配cpu给线程时,是通过时间片轮训方式进行的
	4.进程是操作系统分配程序执行资源的单位,而线程是进程的一个实体,
	 是CPU调度和分配的单位。




2. 两种创建多线程的方式(重点)  

	a> 创建函数并且传入Thread对象中
		"""创建函数并且传入Thread 对象中
		步骤分析:
		 1. 导入模块 threading
		 2. 准备函数,封装任务
		 3. 由threading模块的Thread类创建线程
		     指定一下新创建的线程要执行的代码
		 4. 执行新创建的线程

		"""




import time

import threading



def download_music():

    """模拟下载音乐,需要6秒"""

    for i in range(6):

        time.sleep(1)  # 休眠1秒,模拟耗时的操作1秒

        print("--正在下载音乐...--", i)



def play_music():
    """模拟播放音乐,需要6秒"""
    for i in range(6):
        time.sleep(1)  # 休眠1秒,模拟耗时的操作1秒
        print("--正在播放音乐...--", i)




if name == 'main':

    # 由threading模块的Thread类创建线程 ,指定一下新创建的线程要执行的代码

    # target:目标,指向,表示新创建的线程要执行的代码在target指向的函数中,注意: download_music不要加()

    t1=threading.Thread(target=download_music)  # ctrl+p :查看方法的参数

    t2=threading.Thread(target=play_music)



    # 执行线程

    t1.start()

    t2.start()



	b> 创建Thread类子类,将要执行的代码 写到run函数里面
		1. 导入模块threading
		2. 编写类,继承threading.Thread类
		3. 重写run方法:
		   把子线程要执行的代码放在run方法中
		4. 由自定义的线程类创建在线程对象
		5. 执行子线程

import threading

import time




class MyThread(threading.Thread):

    def run(self):

        """把线程要执行的代码写到该方法中"""

        # print("run----", threading.current_thread().name)

        print("run----", self.name)

        for i in range(3):

            time.sleep(1)

            print("--run--代码块%d"%i)



def main():

    # 由自定义的线程类创建在线程对象

    t1 = MyThread() # ctrl+alt+v : 生成局部变量



    # 执行子线程
    t1.start()  # start内部会调用run方法




if name == 'main':

    #threading.current_thread().name  取得当前执行该代码的线程

    print("main----",threading.current_thread().name)

    main()

三、线程认识加强

 1. 子线程何时开启、何时结束
	""" 子线程何时开启、何时结束
	    1.子线程何时开启,何时执行子线程中的代码
	       在执行了子线程的start方法后,才真正开启线程,并且立即执行子线程中的代码
	    2.子线程何时结束
	       当执行完代码,立即结束
	    3.主线程何时结束
	        主线程等待所有的子线程执行完,再 结束
	    4.thread.join()与threading.enumerate()
	      - threading.enumerate(): 枚举当前的线程情况,返回的是列表
	      - thread.join():
	       thread:线程对象,t1
	      thread.join() : 阻塞当前执行t1.join() 语句的线程(阻塞主线程),等到t1子线程执行完毕后,再解阻塞(主线程),执行后续的代码
	"""
import threading
import time




def work1():

    """任务1"""

    for i in range(3):

        time.sleep(1)  # 休眠1秒,模拟耗时的操作是一秒

        print('--work1--代码块%d,当前线程情况:%s' %(i,len(threading.enumerate())))



def work2():

    """任务2"""

    for i in range(3):

        time.sleep(1)  # 休眠1秒,模拟耗时的操作是一秒

        print('--work2--代码块%d,当前线程情况:%s' % (i, len(threading.enumerate())))



def main():

    print("子线程创建前的线程情况:",threading.enumerate())

    # 通过threading.Thread创建子线程

    t1=threading.Thread(target=work1)

    t2=threading.Thread(target=work2)



    # time.sleep(0.5) # 确保子线程创建完毕
    print("子线程创建后的线程情况:", threading.enumerate())




    # 执行子线程

    t1.start()

    t2.start()



    t2.join()#阻塞当前执行t2.join() 语句的线程(主线程),等到t2子线程执行完毕后,再解阻塞(主线程),再执行后续的代码

    print("主线程最后执行的代码")





if name == 'main':

    main()

	 

2. 线程的执行顺序
	线程执行是无序的

import threading

import time

class MyThread(threading.Thread):
    def run(self):
        """子线程要执行的代码封装在该方法中"""
        for i in range(3):
            time.sleep(1)
            print("代码块%d,当前线程的名称:%s"%(i,self.name))




def task():

    for i in range(3):

        time.sleep(1)

        print("代码块%d,当前线程的名称:%s" % (i, threading.current_thread().name))





if name == 'main':

    # 通过自定义的线程类来创建对线程

    for i in range(10):

        t1=MyThread()

        t1.start()



    # 主线程运行
    task()

四、多线程-共享全局变量

1. 共享全局变量

import threading

g_num = 100  # 全局变量




def work1():

    global g_num  # 声明使用全局变量

    g_num += 1

    print("--work1的g_nums=%d" % g_num)



def work2():

    global g_num  # 声明使用全局变量

    g_num += 1

    print("--work2的g_nums=%d" % g_num)



def work3():

    print("--work3的g_nums=%d" % g_num)



if __name__ == '__main__':
    # 创建子线程
    t1=threading.Thread(target=work1)
    t1.start()
    t1.join() # 阻塞主线程, 当t1线程执行完,再解阻塞,执行后续的代码

    t2=threading.Thread(target=work2)
    t2.start()
    t2.join()

    # 主线程执行
    work3()




2. 列表当作实参传递到线程中
import threading




def work1(nums):

    """

    任务1, 在列表参数中添加'work1'字符串

    :param nums: 列表类型

    """

    nums.append('work1')

    # g_list.append('work1')

    print("--work1---nums=%s"%nums)

    # print("--work1---g_list=%s"%g_list)



def work2(nums):
    """
    任务2, 在列表参数中添加'work2'字符串
    :param nums: 列表类型
    """
    nums.append('work2')
    # print("--work2---nums=%s"%nums)
    print("--work2---g_list=%s" % g_list)

g_list=[11,22] # 全局变量, 列表类型

def main():
    # 创建线程 
    # target: 目标,执行,表示新创建的线程要执行的代码在target指向的函数中
    # args=(g_list,) 传递的参数是传给target指向的函数
    t1=threading.Thread(target=work1,args=(g_list,))
    t1.start()
    t1.join()

    t2=threading.Thread(target=work2,args=(g_list,))
    t2.start()
    t2.join()




if name == 'main':

    main()

五、多线程-共享全局变量-问题

如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确,即会遇到线程安全问题
import threading




g_nums=0  # 全局变量



def task1(loop):

    """任务1:loop循环变量"""

    global  g_nums

    for i in range(loop):

        # g_nums+=1

        temp=g_nums

        g_nums=temp+1

    print("--task1--g_nums=%d"%g_nums)



def task2(loop):

    """任务2:loop循环变量"""

    global  g_nums

    for i in range(loop):

        # g_nums+=1

        temp=g_nums

        g_nums=temp+1

    print("--task2--g_nums=%d"%g_nums)



if name == 'main':

    # 创建线程1

    t1=threading.Thread(target=task1,args=(1000000,))

    t2=threading.Thread(target=task2,args=(1000000,))



    t1.start()
    t2.start()




    t1.join()

    t2.join()

    print("最后的结果:g_nums=%d"%g_nums)

六、同步的概念

同步(synchronize)
	就是指协同步调,按预定的先后次序进行运行。比如:你说完,我再说;你做完,我再做;你执行完,我再执行。




在多线程编程中,一些敏感数据不允许被多个线程同时访问,因为会出现线程安全问题,通过线程同步机制,能保证共享数据在任何时刻,最多有一个线程访问,以保证数据的正确性

线程同步,就是线程的等待

七、互斥锁

同步是一种思想,一种机制

  1. 互斥锁是实现同步机制的一种方案 ,好比多线程是实现多任务的一种方案

  2. 使用互斥锁

    1. 取得一把互斥锁
      mutexLock=threading.Lock()

    2. 上锁
      mutexLock.acquire()

      1. 解锁,释放锁
        mutexLock.release()

    import threading

    g_nums = 0 # 全局变量

    mutexLock = threading.Lock() # 取得锁

    def task1(loop):

     """任务1:loop循环变量"""
    
     global g_nums
    
     for i in range(loop):
    
         # g_nums+=1
    
    
    
         # 上锁
         mutexLock.acquire()
         temp = g_nums
         g_nums = temp + 1
    
         # 释放锁,解锁
         mutexLock.release()
    
     print("--task1--g_nums=%d" % g_nums)
    

    def task2(loop):

     """任务2:loop循环变量"""
    
     global g_nums
    
     for i in range(loop):
    
    
    
         # 上锁
         mutexLock.acquire()
         temp = g_nums
         g_nums = temp + 1
    
         # 释放锁,解锁
         mutexLock.release()
     print("--task2--g_nums=%d" % g_nums)
    

    if name == ‘main’:

     # 创建线程1
    
     t1 = threading.Thread(target=task1, args=(1000000,))
    
     t2 = threading.Thread(target=task2, args=(1000000,))
    
    
    
     t1.start()
     t2.start()
    
     t1.join()
     t2.join()
     print("最后的结果:g_nums=%d" % g_nums)
    

八、多任务版UDP聊天器

要求:
1.通过threading.Thread创建一个子线程,执行recv_msg的函数,用于接收对方发送过来的消息
2.在主线程中调用send_msg函数,采集用户输入的聊天数据并且发送给对方

"""
多任务版UDP聊天器
	要求:
	1.通过threading.Thread创建一个子线程,执行recv_msg的函数,用于接收对方发送过来的消息
	2.在主线程中调用send_msg函数,采集用户输入的聊天数据并且发送给对方
"""
import socket
import threading




def send_msg(udp_socket):

    """发送消息"""

    while True:

        send_data = input("请输入要发送的消息:")

        dest_addr = ('192.168.110.117', 8080)



        # 编码
        data = send_data.encode('utf-8')
        udp_socket.sendto(data, dest_addr)




def recv_msg(udp_socket):

    """接收消息"""

    while True:

        # 等待接收目标主机回传的消息

        recv_data = udp_socket.recvfrom(1024)



        # 解码
        data = recv_data[0].decode('gbk')

        # 显示接收的数据
        print(data)




def main():

    # 1. 创建udp socket对象

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



    # 2.绑定端口
    udp_socket.bind(('', 9999))

    # 3. 通过threading.Thread创建一个子线程,执行recv_msg的函数,用于接收对方发送过来的消息
    t1=threading.Thread(target=recv_msg,args=(udp_socket,))
    t1.start()




    # 4. 在主线程中调用send_msg函数,采集用户输入的聊天数据并且发送给对方

    send_msg(udp_socket)



    # 5.关闭socket
    # udp_socket.close()




if name == 'main':

    main()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值