Python学习笔记之--多线程和多进程使用demo

其他链接:
- OC学习笔记之多线程
- Qt学习笔记之–多线程





由于实际运用中开辟线程和进程 都是在for 循环中进行的,所以不能不考虑同时 并发的线程和进程的数量。所以在下面使用都 有控制并发数的方法。

由于Python 多线程GIL锁的原因,导致cpu处理环节,实际上是串行执行的,导致在CPU处理密集型的 方法中,采用多线程处理却要比单线程处理慢。所以有必要引进 多进程处理方式。

一、threading 启动多线程

多线程在CPU密集型操作中是串行的,多线程使用与I/O操作或者网络请求操作。

  • 使用Semaphore控制并发线程数量
  • threading.Lock() 将子线程数据上锁

模拟代码

import threading
import os
 
def run_multi_thread():

    def long_time_task_in_subThread(_index):
        sem.acquire()
        lock.acquire()
        _dic["num"] += 1
        print("num:"+str(_dic["num"]) + "\t" + str(threading.current_thread()))
        lock.release()
        sem.release()

    _dic = {"num":0}

    sem=threading.Semaphore(8) #控制最多 同时运行 8个线程
    lock=threading.Lock()   #将锁内的代码串行化

    l = []
    for i in range(50):
        t=threading.Thread(target=long_time_task_in_subThread,args=(i,))
        t.start()
        l.append(t)

    for t in l:
        t.join()

    print("---over")

if __name__=='__main__':
    run_multi_thread()

运行结果

num:1	<Thread(Thread-1, started 25320)>
num:2	<Thread(Thread-2, started 25172)>
num:3	<Thread(Thread-3, started 19528)>
num:4	<Thread(Thread-4, started 21772)>
num:5	<Thread(Thread-5, started 12264)>
num:6	<Thread(Thread-6, started 8612)>
num:7	<Thread(Thread-7, started 18588)>
num:8	<Thread(Thread-8, started 15840)>
num:9	<Thread(Thread-9, started 25448)>
num:10	<Thread(Thread-10, started 22076)>
num:11	<Thread(Thread-11, started 20668)>
num:12	<Thread(Thread-12, started 10736)>
num:13	<Thread(Thread-13, started 24532)>
num:14	<Thread(Thread-14, started 13352)>
num:15	<Thread(Thread-15, started 15108)>
num:16	<Thread(Thread-16, started 23400)>
num:17	<Thread(Thread-17, started 20712)>
num:18	<Thread(Thread-18, started 17332)>
num:19	<Thread(Thread-19, started 23236)>
num:20	<Thread(Thread-20, started 24048)>
num:21	<Thread(Thread-21, started 24616)>
num:22	<Thread(Thread-22, started 24956)>
num:23	<Thread(Thread-23, started 25300)>
num:24	<Thread(Thread-24, started 18692)>
num:25	<Thread(Thread-25, started 15256)>
num:26	<Thread(Thread-26, started 24368)>
num:27	<Thread(Thread-27, started 22692)>
num:28	<Thread(Thread-28, started 16732)>
num:29	<Thread(Thread-29, started 22104)>
num:30	<Thread(Thread-30, started 23652)>
num:31	<Thread(Thread-31, started 25560)>
num:32	<Thread(Thread-32, started 25564)>
num:33	<Thread(Thread-33, started 25512)>
num:34	<Thread(Thread-34, started 25568)>
num:35	<Thread(Thread-35, started 23660)>
num:36	<Thread(Thread-36, started 25312)>
num:37	<Thread(Thread-37, started 22084)>
num:38	<Thread(Thread-38, started 25456)>
num:39	<Thread(Thread-39, started 25444)>
num:40	<Thread(Thread-40, started 24512)>
num:41	<Thread(Thread-41, started 25440)>
num:42	<Thread(Thread-42, started 25296)>
num:43	<Thread(Thread-43, started 24816)>
num:44	<Thread(Thread-44, started 25332)>
num:45	<Thread(Thread-45, started 23324)>
num:46	<Thread(Thread-46, started 22316)>
num:47	<Thread(Thread-47, started 7300)>
num:48	<Thread(Thread-48, started 23704)>
num:49	<Thread(Thread-49, started 22928)>
num:50	<Thread(Thread-50, started 11604)>
---over
[Finished in 0.3s]

二、Multiprocessing 的 Pool 启动多进程(控制并发数)

在CPU密集型操作中 多进程优化很明显

  • 多进程使用multiprocessingPool 来控制进程数量,防止进程数量过多 崩溃。
  • 使用Manager 来进行共有数据 同步 ,并加锁
  • Pool().apply_async 进行 子线程调用
from multiprocessing import Pool
import os, time
import multiprocessing as mp

def long_time_task_in_subProcess(_dic, _lock1):
    # 多进程处理中 
    #加入这里是 有 2-3M的字符串 多次查找其中的子字符串,能明显感觉多进程 比 多线程的优势。
    time.sleep(0.01) 
    #防止修改有误,添加共享锁
    _lock1.acquire() 
    _dic["num"] += 1
    _c = _dic["num"]
    _st = "时间:" + str(time.time())+  "    num:"+str(_c) + "    进程ID:" +  str(os.getpid())
    _lock1.release()
    return _st

def run_multi_process():
    _cpuCount = mp.cpu_count()
    m = mp.Manager()
    # _dic 和 _lock1 需要在多进程中共享数据,方便修改。
    _dic = m.dict()
    _lock1 = m.Lock()
    _dic["num"] = 0

    results = []
    p = mp.Pool(processes=_cpuCount*2)
    for i in range(50):
       results.append(p.apply_async(long_time_task_in_subProcess, args=(_dic, _lock1, )))
    p.close()
    p.join()

    print("---over")
    #如果dic 在子进程中进行打印,会发现 打印顺序是乱的,并不是按照时间顺序进行打印的,
    #所以 这里将子进程的时间数据 返回后排序,进一步检测,是否是按照 _dic["num"] 的顺序来进行 +1的。
    _list = [_result.get() for _result in results]
    _list.sort()
    for x in _list:
        print(x)

if __name__=='__main__':
    run_multi_process()


下面是运行结果,可见,最后确实是 num 加到了 50.

---over
---over
时间:1619684812.2807126    num:1    进程ID:5900
时间:1619684812.2817101    num:2    进程ID:10284
时间:1619684812.3116302    num:3    进程ID:21748
时间:1619684812.314622    num:4    进程ID:21444
时间:1619684812.3166168    num:5    进程ID:22472
时间:1619684812.3186111    num:6    进程ID:19680
时间:1619684812.3196087    num:7    进程ID:20152
时间:1619684812.3285844    num:8    进程ID:24156
时间:1619684812.3295817    num:9    进程ID:24404
时间:1619684812.3315783    num:10    进程ID:22448
时间:1619684812.3345685    num:11    进程ID:3112
时间:1619684812.3435445    num:12    进程ID:15376
时间:1619684812.3525202    num:13    进程ID:22192
时间:1619684812.3545163    num:14    进程ID:23164
时间:1619684812.3565097    num:15    进程ID:17076
时间:1619684812.3585036    num:16    进程ID:22272
时间:1619684812.362494    num:17    进程ID:21748
时间:1619684812.3754587    num:18    进程ID:10284
时间:1619684812.3754587    num:19    进程ID:4376
时间:1619684812.3774548    num:20    进程ID:21444
时间:1619684812.3784504    num:21    进程ID:19680
时间:1619684812.3794477    num:22    进程ID:24156
时间:1619684812.3804457    num:23    进程ID:5900
时间:1619684812.3814452    num:24    进程ID:3112
时间:1619684812.39042    num:25    进程ID:15376
时间:1619684812.3924136    num:26    进程ID:23164
时间:1619684812.3924136    num:27    进程ID:22472
时间:1619684812.393412    num:28    进程ID:22272
时间:1619684812.3944092    num:29    进程ID:20152
时间:1619684812.3954053    num:30    进程ID:22192
时间:1619684812.3964021    num:31    进程ID:24404
时间:1619684812.3974    num:32    进程ID:22448
时间:1619684812.4043822    num:33    进程ID:20616
时间:1619684812.4073744    num:34    进程ID:17076
时间:1619684812.4093685    num:35    进程ID:21748
时间:1619684812.4113638    num:36    进程ID:7884
时间:1619684812.4133577    num:37    进程ID:18860
时间:1619684812.4143555    num:38    进程ID:22388
时间:1619684812.417347    num:39    进程ID:24636
时间:1619684812.4243286    num:40    进程ID:21444
时间:1619684812.425326    num:41    进程ID:10284
时间:1619684812.4263227    num:42    进程ID:24156
时间:1619684812.4273195    num:43    进程ID:4376
时间:1619684812.4283168    num:44    进程ID:19680
时间:1619684812.4283168    num:45    进程ID:5900
时间:1619684812.4352984    num:46    进程ID:22272
时间:1619684812.436296    num:47    进程ID:22744
时间:1619684812.436296    num:48    进程ID:22448
时间:1619684812.4372928    num:49    进程ID:3112
时间:1619684812.4382904    num:50    进程ID:22472
[Finished in 1.4s]

三、Multiprocessing的Process 启动进程(不控制并发数)

  • 使用Process 启动进程
    虽然代码简单, 但是不能控制进程并发数量,如果循环量过大,可能导致进程数量开辟过大。
from multiprocessing import Process
import os, time

def run_proc(name):
  print("子进程ID: %s" % (os.getpid()))

if __name__ == '__main__':
    print("主进程ID: %s." % os.getpid())
    l = []
    for x in range(1,10):
        p = Process(target=run_proc, args=(x,))
        p.start()
        l.append(p)

    for t in l:
        t.join()

    print("进程结束")
主进程ID: 6648.
子进程ID: 5668
子进程ID: 20124
子进程ID: 18352
子进程ID: 8104
子进程ID: 15384
子进程ID: 1644
子进程ID: 15840
子进程ID: 18316
子进程ID: 2224
进程结束
[Finished in 0.5s]
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值