Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Einführung in Multithreading in Python (Codebeispiel)

Detaillierte Einführung in Multithreading in Python (Codebeispiel)

不言
不言Original
2018-08-29 10:18:461392Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Einführung in Multithreading in Python (Codebeispiele). Ich hoffe, dass er Ihnen als Referenz dienen wird.

In diesem Artikel werden die beim Erlernen von Python auftretenden Probleme und einige häufige Verwendungen beschrieben. Beachten Sie, dass die Python-Version dieser Entwicklungsumgebung 2.7 ist.

1. Benennung von Python-Dateien

Bei der Benennung von Python-Dateien müssen Sie darauf achten, dass kein Konflikt mit dem Standardmodulnamen des Systems entsteht, da sonst ein Fehler gemeldet wird.
Wie im folgenden Beispiel lautet der Dateiname beim Lernen von Threads threading.py und das Python-Skript ist völlig normal, es wird jedoch der folgende Fehler gemeldet: AttributeError: 'module' object has no attribute 'xxx'.

threading.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_test.py
@time: 18/8/25 09:14
"""

import threading

# 获取已激活的线程数
print(threading.active_count())

Ausführung:

➜  baseLearn python threading/threading.py
Traceback (most recent call last):
  File "threading/threading.py", line 9, in <module>
    import threading
  File "/Users/kaiyiwang/Code/python/baseLearn/threading/threading.py", line 12, in <module>
    print(threading.active_count())
AttributeError: 'module' object has no attribute 'active_count'
➜  baseLearn

Problemort:

Sehen Sie sich die Quelldatei der import-Bibliothek an und finden Sie die Quelldatei existiert und hat keine Fehler, es gibt auch .pyc Dateien mit Quelldateien

Problemlösung:

  • 1 Verwenden Sie beim Benennen des Py-Skripts nicht Python reserviert Wörter oder Modulnamen Das Gleiche wie

  • 2. Löschen Sie die

    -Datei der Bibliothek (da das Py-Skript bei jeder Ausführung eine .pyc-Datei generiert; in dem Fall, in dem die .pyc-Datei bereits generiert wurde Wenn der Code nicht aktualisiert wird, wird pyc beim Ausführen weiterhin verwendet. Löschen Sie daher die .pyc-Datei und führen Sie den Code erneut aus oder suchen Sie eine Umgebung, in der sich der Code befinden kann Führen Sie die .pyc-Datei des aktuellen Computers aus, kopieren Sie sie und ersetzen Sie sie .pyc

Benennen Sie den Namen der Skriptdatei in

um und führen Sie sie dann aus. Es wird kein Fehler gemeldet. threading_test.py

➜  baseLearn python threading/threading_test.py
1
➜  baseLearn
Multi-Threading

Multi-Threading ist eine effektive Möglichkeit, Programmberechnungen zu beschleunigen

ist schnell und einfach zu starten In diesem Abschnitt zeigen wir Ihnen, wie Sie es verwenden. threading

1. Thread hinzufügen

threading_test.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_test.py
@time: 18/8/25 09:14
"""

import threading

# 获取已激活的线程数
# print(threading.active_count())

# 查看所有线程信息
# print(threading.enumerate())

# 查看现在正在运行的线程
# print(threading.current_thread())

def thread_job():
    print('This is a thread of %s' % threading.current_thread())


def main():
    thread = threading.Thread(target=thread_job,)  # 定义线程
    thread.start() # 让线程开始工作

if __name__ == '__main__':
    main()
2. Join-Funktion

Das Ergebnis, wenn join() nicht hinzugefügt wird

Wir lassen die

Thread-Arbeit länger dauern T1

threading_join.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def thread_job():
    print('T1 start\n')
    for i in range(10):
        time.sleep(0.1) # 任务时间0.1s
    print("T1 finish\n")


def main():
    added_thread = threading.Thread(target=thread_job, name='T1')  # 定义线程
    added_thread.start() # 让线程开始工作
    print("all done\n")

if __name__ == '__main__':
    main()
Das erwartete Ausgabeergebnis ist die Ausführung in der folgenden Reihenfolge:

T1 start
T1 finish
all done
Aber das tatsächliche laufende Ergebnis Addieren Sie das Ergebnis von join() an:

➜  baseLearn python threading/threading_join.py
T1 start
all done


T1 finish

➜  baseLearn

Ausgabe

, bevor die Thread-Aufgabe abgeschlossen ist. Wenn Sie der Reihenfolge folgen möchten, können Sie nach dem Starten des Threads all done darauf aufrufen: join

added_thread.start()
added_thread.join()
print("all done\n")
Ergebnisse drucken:

➜  baseLearn python threading/threading_join.py
T1 start

T1 finish

all done
Vollständige Skriptdatei:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def thread_job():
    print('T1 start\n')
    for i in range(10):
        time.sleep(0.1) # 任务时间0.1s
    print("T1 finish\n")


def main():
    added_thread = threading.Thread(target=thread_job, name='T1')  # 定义线程
    added_thread.start() # 让线程开始工作
    added_thread.join()
    print("all done\n")

if __name__ == '__main__':
    main()
Versuchen Sie es it out

Wenn zwei Threads hinzugefügt werden, wie sieht die gedruckte Ausgabe aus? Das von

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def T1_job():
    print('T1 start\n')
    for i in range(10):
        time.sleep(0.1) # 任务时间0.1s
    print("T1 finish\n")

def T2_job():
    print("T2 start\n")
    print("T2 finish\n")

def main():
    thread_1 = threading.Thread(target=T1_job, name='T1')  # 定义线程
    thread_2 = threading.Thread(target=T2_job, name='T2')  # 定义线程
    thread_1.start()  # 开启T1
    thread_2.start()  # 开启T2
    print("all done\n")

if __name__ == '__main__':
    main()
ausgegebene „Eins“-Ergebnis lautet:

T1 start

T2 start

T2 finish

all done

T1 finish
Jetzt hat weder T1 noch T2

. Beachten Sie, dass dort „eins“ steht, da das Auftreten von „all done“ vollständig von zwei abhängt Aufgrund der Ausführungsgeschwindigkeit eines Threads ist es durchaus möglich, dass T2-Finish erscheint, nachdem alles erledigt ist. Wir können diese chaotische Ausführungsmethode nicht tolerieren, daher müssen wir Join verwenden, um sie zu steuern. join

Versuchen wir,

:thread_1.join()

thread_1.start()
thread_1.join() # notice the difference!
thread_2.start()
print("all done\n")
nach dem Start von T1 und vor dem Start von T2 hinzuzufügen. Drucken Sie das Ergebnis aus:

T1 start

T1 finish

T2 start
all done

T2 finish
Wie Sie sehen können, wartet T2 auf T1 Fertig. Habe gerade angefangen zu laufen.

3. Warteschlange zum Speichern von Prozessergebnissen

Implementierungsfunktion

Code zum Implementieren der Funktion, Übergeben der Daten in der Datenliste, Verwenden von vier Threads zur Verarbeitung und Speichern Sie das Ergebnis in der Warteschlange. Nachdem der Thread ausgeführt wurde, erhalten Sie das gespeicherte Ergebnis aus der Warteschlange.

Definieren Sie ein

in der Multithread-Funktion, um den Rückgabewert zu speichern. Queue, definieren Sie ein Multi -Thread-Liste und Initialisierung einer mehrdimensionalen Datenliste, die zur Verarbeitung verwendet wird: 代替return

threading_queue.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_queue.py
@time: 18/8/25 09:14
"""

import threading
import time
from queue import Queue

def job(l, q):
    for i in range(len(l)):
        l[i] = l[i] ** 2
    q.put(l) #多线程调用的函数不能用return返回值

def multithreading():
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []
    data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]

    for i in range(4): #定义四个线程
        t = threading.Thread(target=job, args=(data[i], q))  #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面
        t.start() #开始线程
        threads.append(t) #把每个线程append到线程列表中

    for thread in threads:
        thread.join()

    results = []
    for _ in range(4):
        results.append(q.get()) #q.get()按顺序从q中拿出一个值
    print(results)


if __name__ == '__main__':
    multithreading()
Beim Ausführen des obigen Skripts ist ein Fehler aufgetreten:

➜  baseLearn python threading/threading_queue.py
Traceback (most recent call last):
  File "threading/threading_queue.py", line 11, in <module>
    from queue import Queue
ImportError: No module named queue
Danach Bei der Überprüfung des Grunds wurde festgestellt, dass das Modul durch die Python-Version verursacht wurde. In Python 3 wurde es umbenannt, um den PEP8-Richtlinien zu folgen (alles in Kleinbuchstaben für Modulnamen), sodass die Klasse in der Warteschlange verbleibt alle Versionen (nach PEP8).


Typischerweise würden Sie portierbare Versionsimporte wie folgt schreiben:

In Python3 so zitiert: >

try:
    import queue
except ImportError:
    import Queue as queue
In Python2 können wir Zitat wie folgt:

from Queue import Queue
Drucken:

baseLearn python ./threading/threading_queue.py
[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
Vollständiger Code:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_queue.py
@time: 18/8/25 09:14
"""

import threading
# import time
from Queue import Queue

def job(l, q):
    for i in range(len(l)):
        l[i] = l[i] ** 2
    q.put(l) #多线程调用的函数不能用return返回值

def multithreading():
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []
    data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]

    for i in range(4): #定义四个线程
        t = threading.Thread(target=job, args=(data[i], q))  #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面
        t.start() #开始线程
        threads.append(t) #把每个线程append到线程列表中

    for thread in threads:
        thread.join()

    results = []
    for _ in range(4):
        results.append(q.get()) #q.get()按顺序从q中拿出一个值
    print(results)


if __name__ == '__main__':
    multithreading()
4. GIL-Effizienzproblem


Was ist GIL ? threading_queue.py

Dieses Mal werfen wir einen Blick darauf, warum Pythons Multithreading manchmal nicht besonders ideal ist. Der Hauptgrund ist, dass es einen notwendigen Link im Design von Python gibt, nämlich

. Dieses Ding ermöglicht es Python, immer noch nur eine Sache gleichzeitig zu verarbeiten.

Erklärung von GIL:

尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。 GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。

测试GIL

我们创建一个 job, 分别用 threading 和 一般的方式执行这段程序. 并且创建一个 list 来存放我们要处理的数据. 在 Normal 的时候, 我们这个 list 扩展4倍, 在 threading 的时候, 我们建立4个线程, 并对运行时间进行对比.

threading_gil.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_gil.py
@time: 18/8/25 09:14
"""

import threading
from Queue import Queue
import copy
import time

def job(l, q):
    res = sum(l)
    q.put(l) #多线程调用的函数不能用return返回值

def multithreading(l):
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []

    for i in range(4): #定义四个线程
        t = threading.Thread(target=job, args=(copy.copy(l), q), name="T%i" % i)  #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面
        t.start() #开始线程
        threads.append(t) #把每个线程append到线程列表中

    [t.join() for t in threads]
    total = 0
    for _ in range(4):
        total = q.get() #q.get()按顺序从q中拿出一个值
    print(total)

def normal(l):
    total = sum(l)
    print(total)

if __name__ == '__main__':
    l = list(range(1000000))
    s_t = time.time()
    normal(l*4)
    print('normal:', time.time() - s_t)
    s_t = time.time()
    multithreading(l)
    print('multithreading: ', time.time() - s_t)

如果你成功运行整套程序, 你大概会有这样的输出. 我们的运算结果没错, 所以程序 threading 和 Normal 运行了一样多次的运算. 但是我们发现 threading 却没有快多少, 按理来说, 我们预期会要快3-4倍, 因为有建立4个线程, 但是并没有. 这就是其中的 GIL 在作怪.

1999998000000
normal:  0.10034608840942383
1999998000000
multithreading:  0.08421492576599121

5、线程锁Lock

不使用 Lock 的情况

threading_lock.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_lock.py
@time: 18/8/25 09:14
"""

import threading

# 全局变量A的值每次加1,循环10次,并打印
def job1():
    global A
    for i in range(10):
        A+=1
        print('job1',A)

# 全局变量A的值每次加10,循环10次,并打印
def job2():
    global A
    for i in range(10):
        A+=10
        print('job2',A)

# 定义两个线程,分别执行函数一和函数二
if __name__== '__main__':
   
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

打印输出数据:

➜  baseLearn python ./threading/threading_lock.py
('job1', ('job2'1)
, (11)'job1'
('job2', 22)
('job2', 32)
('job2', 42)
('job2', 52)
('job2', 62)
('job2', 72)
('job2', 82)
('job2', 92)
('job2', 102)
, 12)
('job1', 103)
('job1', 104)
('job1', 105)
('job1', 106)
('job1', 107)
('job1', 108)
('job1', 109)
('job1', 110)

可以看出,打印的结果非常混乱

使用 Lock 的情况

lock在不同线程使用同一共享内存时,能够确保线程之间互不影响,使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行lock.acquire()将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问,执行运算完毕后,使用lock.release()将锁打开, 保证其他的线程可以使用该共享内存。

函数一和函数二加锁

def job1():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=1
        print('job1',A)
    lock.release()

def job2():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=10
        print('job2',A)
    lock.release()

主函数中定义一个Lock

if __name__== '__main__':
    lock=threading.Lock()
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

完整代码:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_lock.py
@time: 18/8/25 09:14
"""

import threading

def job1():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=1
        print('job1',A)
    lock.release()

def job2():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=10
        print('job2',A)
    lock.release()

if __name__== '__main__':
    lock = threading.Lock()
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

打印输出:

➜  baseLearn python ./threading/threading_lock.py
('job1', 1)
('job1', 2)
('job1', 3)
('job1', 4)
('job1', 5)
('job1', 6)
('job1', 7)
('job1', 8)
('job1', 9)
('job1', 10)
('job2', 20)
('job2', 30)
('job2', 40)
('job2', 50)
('job2', 60)
('job2', 70)
('job2', 80)
('job2', 90)
('job2', 100)
('job2', 110)

从打印结果来看,使用lock后,一个一个线程执行完。使用lock和不使用lock,最后打印输出的结果是不同的。

相关推荐:

详解Python中的多线程编程

理解python多线程(python多线程简明教程)

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Multithreading in Python (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn