python 多任务编程-线程(线程和进程的关系)

线程
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码,需要cpu进行调度,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们常说的主线程

说明:程序启动默认会有一个主线程,程序员自己创建的线程可以称为子线程,多线程可以完成多任务

线程的使用:
1、导入线程模块
import threading

2、线程类 Thread 参数说明
Thread([group[,target[,name[,args[,kwargs]]]]])

  • group: 指定线程组,目前只能使用None
  • target: 执行的目标任务名
  • name: 线程名,一般不设置
  • args: 以元组方式给执行任务传参
  • kwargs: 以字典方式给执行任务传参

3、启动线程
启动线程使用start方法

4、多线程完成多任务的代码

import threading
import time


def sing():
    for i in range(3):
        print("正在唱歌..")
        time.sleep(0.2)

def dance():
    for i in range(3):
        print("正在跳舞..")
        time.sleep(0.2)

if __name__ =="__main__":
    thread_sing = threading.Thread(target=sing)
    thread_dance = threading.Thread(target=dance)

    thread_sing.start()
    thread_dance.start()

运行结果:

正在唱歌..
正在跳舞..
正在唱歌..
正在跳舞..
正在跳舞..
正在唱歌..

线程带有参数的使用同进程相同参考上篇文章

5、线程的注意点介绍

  • 线程之间执行是无序的
  • 主线程会等待所有的子线程执行结束后再结束
  • 线程之间共享全局变量
  • 线程之间共享全局变量数据出现错误问题

5.1 验证线程无序的代码:
说明:它是由cpu 调度决定的,cpu 调度哪个线程,哪个线程就先执行,进程也是无序的, 它是由操作系统调度决定的

import threading
import time

def task():
    time.sleep(2)
    # 获取当前线程名称
    print(threading.current_thread().name)

if __name__ == "__main__":
    my_list = []
    for i in range(10):
        sub_thread = threading.Thread(target=task)
        my_list.append(sub_thread)

    # for 循环是很快的几乎等于同时启动
    for i in my_list:
        i.start()

执行结果:

Thread-3
Thread-1
Thread-2
Thread-9
Thread-10
Thread-8
Thread-7
Thread-6
Thread-5
Thread-4

**5.2 默认情况下,**主线程会等待所有的子线程执行完成后再退出,解决办法:

  • 守护主线程: sub_thread.setDaemon(True)

5.3 线程之间共享全局变量

import threading
import time

g_list =[]


def add_data():
    for i in range(4):
        g_list.append(i)
        print("add_data:",i)
    print("添加数据完成",g_list)


def read_data():
    print("读取数据:",g_list)


if __name__ == "__main__":
    thread_add = threading.Thread(target=add_data)
    thread_read = threading.Thread(target=read_data)

    thread_add.start()
    # 线程等待,主线程等待添加数据线程执行完成以后,代码再继续往下执行
    thread_add.join()
    thread_read.start()

运行结果:

add_data: 0
add_data: 1
add_data: 2
add_data: 3
添加数据完成 [0, 1, 2, 3]
读取数据: [0, 1, 2, 3]

5.4 全局变量数据错误的解决办法:

  • 线程等待(join)
  • 互斥锁
    代码示例:
import threading
import time

g_num = 0

# 每循环一次给全局变量+1
def cal_num1():
    # 声明此处加上global 表示要修改全局变量的内存地址
    global g_num

    for i in range(10000):
        g_num+=1
    print("cal_num1:",g_num)

def cal_num2():
    global g_num

    for i in range(10000):
        g_num+=1
    print("cal_num2:", g_num)


if __name__ == "__main__":
    first_thread = threading.Thread(target=cal_num1)
    second_thread = threading.Thread(target=cal_num2)

    # 启动线程执行任务
    first_thread.start()
    # 主线程等待第一个子线程执行完成后再执行(线程同步)
    first_thread.join()
    second_thread.start()

以上代码所示,对全局变量循环+1,若两个线程同时从内存中取g_num的值为1,同时赋值后结果仍旧为2,理论上应该为3,等于是少加了一次。join()函数可以解决此问题,解决方法二就是利用互斥锁,可以参考下一篇文章。


线程和进程的关系

1、关系对比:

  • 线程是依附在进程里面的,没有进程就没有线程
  • 一个进程默认提供一条线程,进程可以创建多个线程

2、区别对比

  • 进程之间不会共享全局变量
  • 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁或线程同步
  • 创建进程的资源开销要比创建线程的资源开销大
  • 进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位
  • 线程不能独立执行,必须依存在进程中
  • 多进程开发比单进程多线程开发稳定性要强

3、优缺点对比

  • 进程优缺点:
    优点:可以用多核
    缺点:资源开销大

  • 线程优缺点:
    优点:资源开销小
    缺点:不能使用多核

4、小结

  • 进程和线程都是完成多任务的一种方式
  • 多进程要比多线程消耗的资源多, 但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉不会影响其他进程
  • 多进程可以使用cpu的多核运行,多线程可以共享全局变量
  • 线程不能单独执行必须依附在进程里面
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值