Python之线程

1.线程简介

(1)线程概念

  • 线程是指进程内的一个执行单元,也是进程内的可调度实体;相当于是进程的一个子任务
  • 是操作系统能够进行运算调度的最小单位

(2)线程与进程

在这里插入图片描述

  • 一个程序中至少有一个进程,而一个进程中也至少有一个线程

2.线程的格式

(1)格式

import threading   #导入threading模块
threading.Thread(target=None, name=None, args=())

(2)主要参数

参数名作用
target需要调用的函数名
name设置线程的名字
args被调用函数所需要的参数(以元组的形式传入)

(3)常用方法

方法名功能
start()启动线程
join()等待子线程结束
setName()设置线程名
getName()返回线程名
setDaemon()设置守护线程

3.线程的创建示例

(1)不设置线程名、被调函数不需要传参

import threading
import time

def task1():
    print(threading.current_thread().name,2)   #current_thread()方法能返回当前线程的对象;current_thread().name方法即能返回当前线程的名称;第个参数是为了区别线程名所做的标号
    for i in range(1,6):
        time.sleep(2)
        print("*****我采摘%s个苹果"%i)

def task2():
    print(threading.current_thread().name, 3)
    for i in range(1,6):
        time.sleep(2)
        print("$$$$$我采摘%s个苹果"%i)

print(threading.current_thread().name,0)
print(time.ctime())

#创建线程
per1=threading.Thread(target=task1)
per2=threading.Thread(target=task2)

#启动线程
per1.start()
per2.start()

#等待线程结束
per1.join()
per2.join()
print(time.ctime())
print(threading.current_thread().name,1)
MainThread 0   #主线程的默认名
Fri Jul  3 16:53:59 2020
Thread-1 2    #线程1的默认名
Thread-2 3    #线程2的默认名
*****我采摘1个苹果
$$$$$我采摘1个苹果
*****我采摘2个苹果
$$$$$我采摘2个苹果
*****我采摘3个苹果
$$$$$我采摘3个苹果
$$$$$我采摘4个苹果
*****我采摘4个苹果
$$$$$我采摘5个苹果
*****我采摘5个苹果
Fri Jul  3 16:54:09 2020
MainThread 1

(2)设置线程名、被调函数需要传参

  • 被调函数参数需要以元组形式传入,否则会出错
import threading
import time

def task1(a):   #task1函数需要传入参数a
    print(threading.current_thread().name,2,a)
    for i in range(1,6):
        time.sleep(2)
        print("*****我采摘%s个苹果"%i)

def task2():
    print(threading.current_thread().name, 3)
    for i in range(1,6):
        time.sleep(2)
        print("$$$$$我采摘%s个苹果"%i)

print(threading.current_thread().name,0)
print(time.ctime())                                           
per1=threading.Thread(target=task1,name="线程1",args=(10,))   #通过name参数来设置线程名;通过args参数,设置被调用函数task1所需传入的参数(参数要以元组的形式传入,否则会出错)
per2=threading.Thread(target=task2)                          #若传入的参数只有一个时,需要加逗号",",否则会被认为传入的参数是整数而不是元组
per2.setName("线程2")   #通过setName方法来设置线程名
per1.start()
per2.start()
per1.join()
per2.join()
print(time.ctime())
print(threading.current_thread().name,1)
MainThread 0
Fri Jul  3 17:58:03 2020
线程1 2 10   #通过name参数更改了线程名为线程1;10为通过args参数传入的被调函数所需参数
线程2 3   #通过setName方法更改了线程名线程2
*****我采摘1个苹果
$$$$$我采摘1个苹果
*****我采摘2个苹果
$$$$$我采摘2个苹果
*****我采摘3个苹果
$$$$$我采摘3个苹果
*****我采摘4个苹果
$$$$$我采摘4个苹果
*****我采摘5个苹果
$$$$$我采摘5个苹果
Fri Jul  3 17:58:13 2020
MainThread 1

(3)不写 join()方法

  • 主线程不会等待子线程结束,当主线程执行完自己的任务就直接结束,而子线程继续执行自己的任务
import threading
import time

def task1(a):
    print(threading.current_thread().name,2,a)
    for i in range(1,6):
        time.sleep(2)
        print("*****我采摘%s个苹果"%i)

def task2():
    print(threading.current_thread().name, 3)
    for i in range(1,6):
        time.sleep(2)
        print("$$$$$我采摘%s个苹果"%i)

print(threading.current_thread().name,0)
print(time.ctime())
per1=threading.Thread(target=task1,name="线程1",args=(10,))
per2=threading.Thread(target=task2)
per2.setName("线程2")
per1.start()
per2.start()
print(time.ctime())
print(threading.current_thread().name,1)
MainThread 0
Fri Jul  3 18:19:31 2020
线程1 2 10
线程2 3
Fri Jul  3 18:19:31 2020
MainThread 1   #主线程不会等待子线程结束,当主线程执行完自己的任务就直接结束

*****我采摘1个苹果   #子线程执行自己的任务
$$$$$我采摘1个苹果
*****我采摘2个苹果
$$$$$我采摘2个苹果
*****我采摘3个苹果
$$$$$我采摘3个苹果
$$$$$我采摘4个苹果
*****我采摘4个苹果
*****我采摘5个苹果
$$$$$我采摘5个苹果

(4)守护线程

  • 守护线程就是主线程不管该线程的执行情况,只要其它子线程结束以及主线程执行结束,程序直接结束,守护线程也直接结束
import threading
import time

def task1(a):
    print(threading.current_thread().name,2,a)
    for i in range(1,6):
        time.sleep(2)
        print("*****我采摘%s个苹果"%i)

def task2():
    print(threading.current_thread().name, 3)
    for i in range(1,6):
        time.sleep(2)
        print("$$$$$我采摘%s个苹果"%i)

print(threading.current_thread().name,0)
print(time.ctime())
per1=threading.Thread(target=task1,name="线程1",args=(10,))
per2=threading.Thread(target=task2)
per2.setName("线程2")

#将两个子线程都设为守护线程
per1.setDaemon(True)
per2.setDaemon(True)
per1.start()
per2.start()
print(time.ctime())
print(threading.current_thread().name,1)
MainThread 0
Sat Jul  4 13:02:58 2020
线程1 2 10
线程2Sat Jul  4 13:02:58 2020
MainThread 1   #执行完主线程就直接结束,不会等待两个守护线程结束

4.锁线程实例

(1)不加锁

import threading

blance = 0

def access(n):   #存取函数,存入n,再取出n
    global blance
    blance = blance + n
    blance = blance - n

def task(arg, n):
    while arg > 0:
         try:
            access(n)
        finally:
            arg = arg - 1

#创建线程
t1 = threading.Thread(target=task, args=(40000, 5))   #被调函数的第一个参数为操作次数,第二个参数为每次存取的钱数;t1任务:进行40000次的存5元,取5元
t2 = threading.Thread(target=task, args=(40000, 8))   #t2任务:进行40000次的存8元,取8元
t1.start()
t2.start()
t1.join()
t2.join()
print(blance)
  • 多次运行后,得出的结果分别为:0,0,0,5,0,0,-8,0,5,5,0
  • 由上可知,若多个线程不加锁,就有可能多个线程同时修改某一个变量,会将数据改乱,得出的结果会与实际结果不同

(2)加锁

  • 通过 threading.RLock() 方法来创建锁,之后通过 acquire() 方法来获取锁,release() 方法释放锁
import threading

blance = 0
lock = threading.RLock()   #通过threading.RLock()方法来建立一个锁;锁不能在存取函数中建立,否则就达不到锁的效果

def access(n):
    global blance
    blance = blance + n
    blance = blance - n

def task(arg, n):
    while arg > 0:
        lock.acquire()   #获得锁
        try:
            access(n)
        finally:
            lock.release()   #释放锁
            arg = arg - 1

t1 = threading.Thread(target=task, args=(40000, 5))   #t1任务:进行40000次的存5元,取5元
t2 = threading.Thread(target=task, args=(40000, 8))   #t2任务:进行40000次的存8元,取8元
t1.start()
t2.start()
t1.join()
t2.join()
print(blance)
  • 无论程序执行多少次,结果都是0,与实际结果相同
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值