python3多线程

  • 先看一个单线程的例子
    在程序的主线程中依次执行两个任务,任务1时间为4秒,任务2时间为2秒。
    由于两个任务是依次执行,从控制台输出可以看出所有任务完成总共花费了6秒。
import time;

#定义任务1
def work1():
    print("任务1开始了:",time.ctime());
    time.sleep(4)
    print("任务1结束了:",time.ctime());
#定义任务2
def work2():
    print("任务2开始了:", time.ctime());
    time.sleep(2)
    print("任务2结束了:", time.ctime());
#定义主程序 在主程序中依次执行两个任务
def main():
    print("主函数开始了:",time.ctime());
    work1();
    work2();
    print("主函数结束了:", time.ctime());
#执行主程序
main();

# 
# 主函数开始了: Sat Feb  2 13:23:01 2019
# 任务1开始了: Sat Feb  2 13:23:01 2019
# 任务1结束了: Sat Feb  2 13:23:05 2019
# 任务2开始了: Sat Feb  2 13:23:05 2019
# 任务2结束了: Sat Feb  2 13:23:07 2019
# 主函数结束了: Sat Feb  2 13:23:07 2019
  • 再将上述例子改成多线程
    -在主程序中单独开启两个线程,将两个任务分别放在两个线程中去执行。这样两个任务并发执行,可以看出所有任务完成花费了4秒。因为在执行任务1时任务2也在执行,两个任务之间不需要等待。
    -使用_thread包来实现多线程
import time;
import _thread;

#任务1
def work1():
    print("任务1开始了:{0}\n".format(time.ctime()));
    time.sleep(4)
    print("任务1结束了:",time.ctime());
#任务2
def work2():
    print("任务2开始了:{0}\n".format(time.ctime()));
    time.sleep(2)
    print("任务2结束了:", time.ctime());

#主程序
def main():
    print("主函数开始了:",time.ctime());
    #开启两个线程来分别执行两个任务
    _thread.start_new_thread(work1,());
    _thread.start_new_thread(work2,());
    print("主函数结束了:", time.ctime());

    #主程序等待20秒再退出,防止两个线程还没开始执行整个程序就终止了
    for i in range(20):
        time.sleep(1);


main();


# 
# 主函数开始了: Sat Feb  2 13:36:29 2019
# 主函数结束了: Sat Feb  2 13:36:29 2019
# 任务1开始了:Sat Feb  2 13:36:29 2019
# 任务2开始了:Sat Feb  2 13:36:29 2019
# 任务2结束了: Sat Feb  2 13:36:31 2019
# 任务1结束了: Sat Feb  2 13:36:33 2019
  • 使用threading模块实现多线程
    -t = threading.Thread(target=xxx,args=(xxx,) //创建子线程
    -t.start() //启动子线程
    -t.join() //主程序等待子线程执行完毕再退出
import time;
import threading;

def work1(in1):
    print("任务1开始啦:{0}\n".format(time.ctime()));
    print("我是参数",in1);
    time.sleep(4);
    print("任务1结束了:{0}".format(time.ctime()));


def work2(in1,in2):
    print("任务1开始啦:{0}\n".format(time.ctime()));
    print("我是参数",in1,in2);
    time.sleep(2);
    print("任务2结束了:{0}".format(time.ctime()));

def main():
    print("主程序开始啦:{0}".format(time.ctime()));
    #创建子线程
    t1 = threading.Thread(target=work1,args=("二狗子",));
    t2 = threading.Thread(target=work2, args=("傻子","大笨蛋"));
    
    #启动子线程
    t1.start();
    t2.start();

    #等子线程执行完毕再退出
    t1.join();
    t2.join();
    print("主程序结束了:{0}".format(time.ctime()));
main();

# 主程序开始啦:Sat Feb  2 14:26:33 2019
# 任务1开始啦:Sat Feb  2 14:26:33 2019
# 
# 我是参数 二狗子
# 任务1开始啦:Sat Feb  2 14:26:33 2019
# 
# 我是参数 傻子 大笨蛋
# 任务2结束了:Sat Feb  2 14:26:35 2019
# 任务1结束了:Sat Feb  2 14:26:37 2019
# 主程序结束了:Sat Feb  2 14:26:37 2019
  • 守护线程与非守护线程
    -当主线程执行完毕时,守护线程也会中止执行(陪葬),而非守护线程与主线程是彼此独立的,主线程结束之后非守护线程会继续执行。
    -创建一个非守护线程
    由输出结果可以看出当主线程结束时,子线程还在休眠,而子线程休眠过后会继续执行。
import time;
import threading;

def fun():
    print("子线程开始执行");
    time.sleep(4);
    print("子线程执行完毕");

print("主线程开始执行");

#执行守护线程
t = threading.Thread(target=fun,args=());
t.start();

#主线程休眠两秒
time.sleep(2);
print("主线程执行结束");

主线程开始执行
子线程开始执行
主线程执行结束
子线程执行完毕

-创建一个守护线程
通过setDaemon(True)函数设置守护线程
由输出结果可以看出当主线程结束时,子线程还在休眠,但子线程休眠过后不会执行,它会随着主线程的中止而中止。

import time;
import threading;

def fun():
    print("子线程开始执行");
    time.sleep(4);
    print("子线程执行完毕");

print("主线程开始执行");

#执行守护线程
t = threading.Thread(target=fun,args=());
t.setDaemon(True);
t.start();

#主线程休眠两秒
time.sleep(2);
print("主线程执行结束");

主线程开始执行
子线程开始执行
主线程执行结束
  • 直接继承threading.Thread来实现多线程
    重写threading.Thread中的run方法
    子类的实例便可以直接运行
import time;
import threading;

class mythread(threading.Thread):
    def __init__(self,arg):
        threading.Thread.__init__(self);
        self.arg = arg;

    def run(self):
        time.sleep(2);
        print(self.arg);

for i in range(5):
    t = mythread(i);
    t.start();
    t.join();

print("主线程执行完毕!");

# 
# 0
# 1
# 2
# 3
# 4
# 主线程执行完毕!

  • 线程间共享变量问题
    当多个线程在同一时刻同时访问同一个变量时,就会产生不可预期的错误,这称为共享变量问题。
  • 先看一个例子
    定义一个全局变量,任务1是引用这个变量让它自增10次,任务2是引用这个变量让它自减十次。
    让这两个任务依次执行,输出结果如预期所料,count从0增加到10,再依次减到0,最后count值还是0.
#
import threading;

count = 0;

def work1():
    global count;
    for i in range(0,10):
        count = count + 1;
        print("任务1:count={0}".format(count));
    

def work2():
    global count;
    for i in range(0,10):
        count = count - 1;
        print("任务2:count={0}".format(count));
    

work1();
work2();
# 
# 任务1:count=1
# 任务1:count=2
# 任务1:count=3
# 任务1:count=4
# 任务1:count=5
# 任务1:count=6
# 任务1:count=7
# 任务1:count=8
# 任务1:count=9
# 任务1:count=10
# 任务2:count=9
# 任务2:count=8
# 任务2:count=7
# 任务2:count=6
# 任务2:count=5
# 任务2:count=4
# 任务2:count=3
# 任务2:count=2
# 任务2:count=1
# 任务2:count=0
  • 把这个例子改进一下下
    把上述两个任务放到两个并发的线程中去执行,看看运行结果。
    从输出数据看没有发生变量冲突,两个线程交叉执行,最终结果仍然是0.
    这主要是因为CPU的执行速度相对于io操作来说实在是太快了,发生变量访问冲突仍然是极小概率事件。
#
import threading;

count = 0;

def work1():
    global count;
    for i in range(0,10):
        count = count + 1;
        print("任务1:count={0}\n".format(count));


def work2():
    global count;
    for i in range(0,10):
        count = count - 1;
        print("任务2:count={0}\n".format(count));


t1 = threading.Thread(target=work1,args=());
t2 = threading.Thread(target=work2, args=());

#启动子线程
t1.start();
t2.start();

#等子线程执行完毕再退出
t1.join();
t2.join();

print("最终结果:{0}".format(count))

任务1:count=1
任务1:count=2
任务1:count=3
任务1:count=4
任务1:count=5
任务2:count=4
任务1:count=5
任务2:count=4
任务1:count=5
任务1:count=6
任务1:count=7
任务2:count=6
任务1:count=7
任务2:count=6
任务2:count=5
任务2:count=4
任务2:count=3
任务2:count=2
任务2:count=1
任务2:count=0
最终结果:0
  • 这次我们把work函数中的print函数去掉,让变量count自增,自减1000000次。
    这次我们再看输出结果就会发现,count的最终值几乎不会是0,而且每一次运行的结果都相差很大,这就证明了多线程共享变量的问题确实是存在的。
#
import threading;

count = 0;

def work1():
    global count;
    for i in range(0,1000000):
        count = count + 1;
        # print("任务1:count={0}\n".format(count));


def work2():
    global count;
    for i in range(0,1000000):
        count = count - 1;
        # print("任务2:count={0}\n".format(count));


t1 = threading.Thread(target=work1,args=());
t2 = threading.Thread(target=work2, args=());

#启动子线程
t1.start();
t2.start();

#等子线程执行完毕再退出
t1.join();
t2.join();

print("最终结果:{0}".format(count))

>>>
最终结果:-194244
  • 通过加锁来解决共享变量问题
    锁其实是一个标志,它表示一个变量正在占用一些资源。
    当一个线程要访问共享资源前,先申请锁,等访问结束后再释放锁。
    当一个线程申请锁后,其他线程就不能访问这个共享资源,只有等待这个线程释放锁之后才能访问。
  • 利用加锁对上个例子进行改善
#
import threading;

count = 0;
#创建一个lock对象
lock = threading.Lock();
def work1():
    global count;
    for i in range(0,1000000):
        #申请锁
        lock.acquire();
        count = count + 1;
        #释放锁
        lock.release();
        # print("任务1:count={0}\n".format(count));


def work2():
    global count;
    for i in range(0,1000000):
        #申请锁
        lock.acquire();
        count = count - 1;
        #释放锁
        lock.release();
        # print("任务2:count={0}\n".format(count));


t1 = threading.Thread(target=work1,args=());
t2 = threading.Thread(target=work2, args=());

#启动子线程
t1.start();
t2.start();

#等子线程执行完毕再退出
t1.join();
t2.join();

print("最终结果:{0}".format(count))
  • 死锁问题
    当线程1占用了锁1,线程2占用了锁2,此时两个线程都想申请对方的锁,都在等着申请到对方的锁才肯释放自己的锁,这样两个线程就会一直盲等下去,产生死锁问题
import threading;
import time;
lock1 = threading.Lock();
lock2 = threading.Lock();

def work1():
    print("-----线程1开始运行");
    lock1.acquire();
    print("线程1申请了锁1");
    time.sleep(1);
    print("线程1等待其他线程释放锁2");
    lock2.acquire();
    print("线程1申请了锁2");
    # time.sleep(1);

    lock2.release();
    print("线程1释放了锁2");
    lock1.release();
    print("线程1释放了锁1");


def work2():
    print("-----线程2开始运行");
    lock2.acquire();
    print("线程2申请了锁2");
    time.sleep(1);
    print("线程2等待其他线程释放锁1");
    lock1.acquire();
    print("线程2申请了锁1");
    # time.sleep(2);

    lock2.release();
    print("线程2释放了锁1");
    lock1.release();
    print("线程2释放了锁2");

if __name__ == '__main__':
    print("主程序启动")
    t1 = threading.Thread(target=work1, args=());
    t2 = threading.Thread(target=work2, args=());

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    print("主程序结束");
主程序启动
-----线程1开始运行
线程1申请了锁1
-----线程2开始运行
线程2申请了锁2
线程1等待其他线程释放锁2
线程2等待其他线程释放锁1
  • 死锁问题的解决
    最简单的办法就是,尽量不要让一个线程申请多个锁。或者设置时间限制,如果线程占用锁太长时间就中止这个线程。
    也可以参照操作系统中死锁问题的解决办法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值