파이썬의 스레딩을 이해하려고합니다. 설명서와 예제를 살펴 봤지만 솔직히 말하면 많은 예제가 지나치게 정교하여 이해하기가 어렵습니다.
멀티 스레딩으로 분할 된 작업을 어떻게 명확하게 보여줍니까?
파이썬의 스레딩을 이해하려고합니다. 설명서와 예제를 살펴 봤지만 솔직히 말하면 많은 예제가 지나치게 정교하여 이해하기가 어렵습니다.
멀티 스레딩으로 분할 된 작업을 어떻게 명확하게 보여줍니까?
답변:
이 질문은 2010 년에 요청 된 이후 map 과 pool을 사용 하여 Python으로 간단한 멀티 스레딩을 수행하는 방법이 실제로 단순화되었습니다 .
아래 코드는 기사 / 블로그 게시물 에서 제공합니다. 한 줄에있는 병렬 처리 : 일일 스레딩 작업을위한 더 나은 모델 . 아래에 요약하겠습니다. 몇 줄의 코드로 끝납니다.
from multiprocessing.dummy import Pool as ThreadPool
pool = ThreadPool(4)
results = pool.map(my_function, my_array)
멀티 스레드 버전은 다음과 같습니다.
results = []
for item in my_array:
results.append(my_function(item))
기술
맵은 멋진 작은 기능이며, 파이썬 코드에 병렬 처리를 쉽게 주입하는 데 핵심입니다. 익숙하지 않은 사람들에게지도는 Lisp와 같은 기능적 언어에서 해제 된 것입니다. 시퀀스에 다른 함수를 매핑하는 함수입니다.
Map은 시퀀스를 반복 처리하고 함수를 적용하며 모든 결과를 끝에 편리한 목록에 저장합니다.
이행
병렬 버전의 맵 함수는 두 개의 라이브러리, 즉 멀티 프로세싱과 잘 알려지지는 않았지만 환상적인 단계 자식 인 multiprocessing.dummy에 의해 제공됩니다.
multiprocessing.dummy
멀티 프로세싱 모듈과 정확히 동일 하지만 대신 스레드를 사용합니다 ( 중요한 차이점 은 CPU를 많이 사용하는 작업에 여러 프로세스를 사용하고 I / O에 대한 스레드를 사용하는 동안 ).
multiprocessing.dummy는 멀티 프로세싱의 API를 복제하지만 스레딩 모듈을 둘러싸는 래퍼에 지나지 않습니다.
import urllib2
from multiprocessing.dummy import Pool as ThreadPool
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
# Make the Pool of workers
pool = ThreadPool(4)
# Open the URLs in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
# Close the pool and wait for the work to finish
pool.close()
pool.join()
그리고 타이밍 결과 :
Single thread: 14.4 seconds
4 Pool: 3.1 seconds
8 Pool: 1.4 seconds
13 Pool: 1.3 seconds
여러 인수 전달 ( Python 3.3 이상에서만 작동 ) :
여러 배열을 전달하려면
results = pool.starmap(function, zip(list_a, list_b))
또는 상수와 배열을 전달하려면
results = pool.starmap(function, zip(itertools.repeat(constant), list_a))
이전 버전의 Python을 사용하는 경우이 해결 방법을 통해 여러 인수를 전달할 수 있습니다 .
( 유용한 의견 을 주신 user136036 에게 감사합니다 .)
간단한 예를 들면 다음과 같습니다. 몇 가지 대체 URL을 시도하고 첫 번째 URL의 내용을 반환하여 응답해야합니다.
import Queue
import threading
import urllib2
# Called by each thread
def get_url(q, url):
q.put(urllib2.urlopen(url).read())
theurls = ["http://google.com", "http://yahoo.com"]
q = Queue.Queue()
for u in theurls:
t = threading.Thread(target=get_url, args = (q,u))
t.daemon = True
t.start()
s = q.get()
print s
이는 스레딩이 간단한 최적화로 사용되는 경우입니다. 각 서브 스레드는 해당 내용을 큐에 놓기 위해 URL이 해결되고 응답하기를 기다리고 있습니다. 각 스레드는 데몬입니다 (메인 스레드가 종료되는 경우 프로세스를 유지하지 않음). 기본 스레드는 모든 하위 스레드를 시작 get
하고 대기열에서 하나를 수행 할 때까지 대기 한 put
다음 결과를 내보내고 종료합니다 (데몬 스레드이기 때문에 여전히 실행중인 하위 스레드를 중단합니다).
파이썬에서 스레드의 적절한 사용은 I / O 작업에 항상 연결되어 있습니다 (CPython은 여러 코어를 사용하여 CPU 바인딩 작업을 실행하지 않기 때문에 스레딩의 유일한 이유는 일부 I / O를 기다리는 동안 프로세스를 차단하지 않음) ). 대기열은 거의 항상 작업을 스레드로 팜핑하거나 작업 결과를 수집하는 가장 좋은 방법이며 본질적으로 스레드 안전하므로 잠금, 조건, 이벤트, 세마포 및 기타 인터에 대해 걱정할 필요가 없습니다. 스레드 조정 / 통신 개념.
join()
메소드 를 사용 하는 것입니다. 메인 스레드가 끊임없이 프로세서를 소비하지 않고 완료 될 때까지 기다릴 수 있기 때문입니다. 값을 확인하십시오. @ Alex : 감사합니다. 이것은 스레드 사용법을 이해하는 데 정확히 필요한 것입니다.
Queue
모듈 이름을로 바꿉니다 queue
. 메소드 명은 동일합니다.
s = q.get()
print s
@ krs013 join
Queue.get ()이 차단 되어 있기 때문에 필요하지 않습니다 .
참고 : 파이썬에서 실제로 병렬화하려면 멀티 프로세싱 모듈을 사용하여 병렬로 실행되는 여러 프로세스를 포크해야합니다 (글로벌 인터프리터 잠금으로 인해 파이썬 스레드는 인터리빙을 제공하지만 실제로는 병렬이 아닌 직렬로 실행되며 단지 I / O 작업을 인터리브 할 때 유용합니다.
그러나 인터리빙 만 찾고 있거나 전역 인터프리터 잠금에도 불구하고 병렬화 할 수있는 I / O 작업을 수행하려는 경우 스레딩 모듈이 시작됩니다. 실제로 간단한 예로, 하위 범위를 병렬로 합산하여 넓은 범위를 합산하는 문제를 고려해 보겠습니다.
import threading
class SummingThread(threading.Thread):
def __init__(self,low,high):
super(SummingThread, self).__init__()
self.low=low
self.high=high
self.total=0
def run(self):
for i in range(self.low,self.high):
self.total+=i
thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join() # This waits until the thread has completed
thread2.join()
# At this point, both threads have completed
result = thread1.total + thread2.total
print result
위의 내용은 I / O가 전혀 없으며 전역 인터프리터 잠금으로 인해 CPython 에서 인터리브 전환 (컨텍스트 전환의 오버 헤드가 추가되었지만) 직렬로 실행되므로 매우 어리석은 예 입니다.
thread1
메인 스레드가 차단되는 동안 완료 될 때까지 실행 한 다음와 동일한 일이 발생 thread2
하고 메인 스레드가 재개되어 누적 된 값을 인쇄합니다.
super(SummingThread, self).__init__()
않습니까? stackoverflow.com/a/2197625/806988
언급 한 다른 사람들과 마찬가지로 CPython은 GIL 때문에 I / O 대기에만 스레드를 사용할 수 있습니다 .
CPU 바인딩 작업에 여러 코어를 활용하려면 멀티 프로세싱을 사용하십시오 .
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
f
기능으로 시작하는 프로세스입니다 . 동시에 메인 프로그램은 프로세스가 종료 join
될 때까지 기다립니다 . 주요 부분이 방금 종료 된 경우 서브 프로세스가 완료되거나 완료되지 않을 수 있으므로 join
항상 수행하는 것이 좋습니다.
map
기능 을 포함하는 확장 된 답변은 다음과 같습니다. stackoverflow.com/a/28463266/2327328
참고 사항 : 스레딩에는 큐가 필요하지 않습니다.
이것은 10 개의 프로세스가 동시에 실행되는 것을 보여줄 수있는 가장 간단한 예입니다.
import threading
from random import randint
from time import sleep
def print_number(number):
# Sleeps a random 1 to 10 seconds
rand_int_var = randint(1, 10)
sleep(rand_int_var)
print "Thread " + str(number) + " slept for " + str(rand_int_var) + " seconds"
thread_list = []
for i in range(1, 10):
# Instantiates the thread
# (i) does not make a sequence, so (i,)
t = threading.Thread(target=print_number, args=(i,))
# Sticks the thread in a list so that it remains accessible
thread_list.append(t)
# Starts threads
for thread in thread_list:
thread.start()
# This blocks the calling thread until the thread whose join() method is called is terminated.
# From http://docs.python.org/2/library/threading.html#thread-objects
for thread in thread_list:
thread.join()
# Demonstrates that the main process waited for threads to complete
print "Done"
for
루프 가 필요하지 않으므로 thread.start()
첫 번째 루프를 호출 할 수 있습니다 .
Alex Martelli의 답변이 도움이되었습니다. 그러나 여기에 더 유용하다고 생각되는 수정 된 버전이 있습니다 (적어도 나에게).
업데이트 : Python 2와 Python 3 모두에서 작동
try:
# For Python 3
import queue
from urllib.request import urlopen
except:
# For Python 2
import Queue as queue
from urllib2 import urlopen
import threading
worker_data = ['http://google.com', 'http://yahoo.com', 'http://bing.com']
# Load up a queue with your data. This will handle locking
q = queue.Queue()
for url in worker_data:
q.put(url)
# Define a worker function
def worker(url_queue):
queue_full = True
while queue_full:
try:
# Get your data off the queue, and do some work
url = url_queue.get(False)
data = urlopen(url).read()
print(len(data))
except queue.Empty:
queue_full = False
# Create as many threads as you want
thread_count = 5
for i in range(thread_count):
t = threading.Thread(target=worker, args = (q,))
t.start()
import Queue ModuleNotFoundError: No module named 'Queue'
내가 파이썬 3.6.5을 실행하고 일부 게시물은 파이썬 3.6.5에서 그게입니다 언급 queue
하지만 난 그것을 변경 후에도 여전히 작동하지 않습니다
함수가 주어지면 f
다음과 같이 스레드하십시오.
import threading
threading.Thread(target=f).start()
인수를 전달하려면 f
threading.Thread(target=f, args=(a,b,c)).start()
is_alive
방법을, 그러나 나는 스레드에 적용하는 방법을 알아낼 수 없었다. 나는 thread1=threading.Thread(target=f).start()
그것을 할당 하고 확인 하려고 thread1.is_alive()
했지만 thread1
로 채워 None
졌으므로 운이 없다. 스레드에 액세스하는 다른 방법이 있는지 알고 있습니까?
thread1=threading.Thread(target=f)
합니다 thread1.start()
. 그럼 당신은 할 수 있습니다 thread1.is_alive()
.
thread1.is_alive()
False
함수가 종료 되 자마자 리턴 으로 합니다 .
나는 이것이 매우 유용하다는 것을 발견했다 : 코어만큼 많은 스레드를 만들고 많은 수의 작업 (이 경우 쉘 프로그램 호출)을 실행하게하십시오.
import Queue
import threading
import multiprocessing
import subprocess
q = Queue.Queue()
for i in range(30): # Put 30 tasks in the queue
q.put(i)
def worker():
while True:
item = q.get()
# Execute a task: call a shell program and wait until it completes
subprocess.call("echo " + str(item), shell=True)
q.task_done()
cpus = multiprocessing.cpu_count() # Detect number of cores
print("Creating %d threads" % cpus)
for i in range(cpus):
t = threading.Thread(target=worker)
t.daemon = True
t.start()
q.join() # Block until all tasks are done
파이썬 3에는 병렬 작업 을 시작 하는 기능이 있습니다. 이것은 우리의 일을 더 쉽게 만듭니다.
다음은 통찰력을 제공합니다.
ThreadPoolExecutor 예제 ( source )
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
ProcessPoolExecutor ( 소스 )
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
획기적인 새로운 동시 . 퓨처 모듈 사용
def sqr(val):
import time
time.sleep(0.1)
return val * val
def process_result(result):
print(result)
def process_these_asap(tasks):
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = []
for task in tasks:
futures.append(executor.submit(sqr, task))
for future in concurrent.futures.as_completed(futures):
process_result(future.result())
# Or instead of all this just do:
# results = executor.map(sqr, tasks)
# list(map(process_result, results))
def main():
tasks = list(range(10))
print('Processing {} tasks'.format(len(tasks)))
process_these_asap(tasks)
print('Done')
return 0
if __name__ == '__main__':
import sys
sys.exit(main())
executor 접근 방식은 이전에 Java에 익숙하지 않은 모든 사람들에게 친숙하게 보일 수 있습니다.
또한 참고 사항 : 우주를 제정신 상태로 유지하려면 with
컨텍스트를 사용하지 않는 경우 풀 / 실행기를 닫는 것을 잊지 마십시오 (매우 훌륭합니다)
나에게 스레딩의 완벽한 예는 비동기 이벤트를 모니터링하는 것입니다. 이 코드를보십시오.
# thread_test.py
import threading
import time
class Monitor(threading.Thread):
def __init__(self, mon):
threading.Thread.__init__(self)
self.mon = mon
def run(self):
while True:
if self.mon[0] == 2:
print "Mon = 2"
self.mon[0] = 3;
IPython 세션 을 열고 다음과 같은 작업을 수행 하여이 코드를 사용할 수 있습니다 .
>>> from thread_test import Monitor
>>> a = [0]
>>> mon = Monitor(a)
>>> mon.start()
>>> a[0] = 2
Mon = 2
>>>a[0] = 2
Mon = 2
몇 분 기다려
>>> a[0] = 2
Mon = 2
대부분의 문서와 튜토리얼은 Python Threading
과 Queue
모듈을 사용 하므로 초보자에게는 압도적 인 것처럼 보일 수 있습니다.
아마도 concurrent.futures.ThreadPoolExecutor
파이썬 3 의 모듈을 고려할 것 입니다.
with
절과 목록 이해력과 결합 하면 진정한 매력이 될 수 있습니다.
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_url(url):
# Your actual program here. Using threading.Lock() if necessary
return ""
# List of URLs to fetch
urls = ["url1", "url2"]
with ThreadPoolExecutor(max_workers = 5) as executor:
# Create threads
futures = {executor.submit(get_url, url) for url in urls}
# as_completed() gives you the threads once finished
for f in as_completed(futures):
# Get the results
rs = f.result()
실제 작업이 수행되지 않은 많은 예제를 보았으며 대부분 CPU에 바인딩되었습니다. 다음은 천만에서 천만 사이의 모든 소수를 계산하는 CPU 바운드 작업의 예입니다. 여기 네 가지 방법을 모두 사용했습니다.
import math
import timeit
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def time_stuff(fn):
"""
Measure time of execution of a function
"""
def wrapper(*args, **kwargs):
t0 = timeit.default_timer()
fn(*args, **kwargs)
t1 = timeit.default_timer()
print("{} seconds".format(t1 - t0))
return wrapper
def find_primes_in(nmin, nmax):
"""
Compute a list of prime numbers between the given minimum and maximum arguments
"""
primes = []
# Loop from minimum to maximum
for current in range(nmin, nmax + 1):
# Take the square root of the current number
sqrt_n = int(math.sqrt(current))
found = False
# Check if the any number from 2 to the square root + 1 divides the current numnber under consideration
for number in range(2, sqrt_n + 1):
# If divisible we have found a factor, hence this is not a prime number, lets move to the next one
if current % number == 0:
found = True
break
# If not divisible, add this number to the list of primes that we have found so far
if not found:
primes.append(current)
# I am merely printing the length of the array containing all the primes, but feel free to do what you want
print(len(primes))
@time_stuff
def sequential_prime_finder(nmin, nmax):
"""
Use the main process and main thread to compute everything in this case
"""
find_primes_in(nmin, nmax)
@time_stuff
def threading_prime_finder(nmin, nmax):
"""
If the minimum is 1000 and the maximum is 2000 and we have four workers,
1000 - 1250 to worker 1
1250 - 1500 to worker 2
1500 - 1750 to worker 3
1750 - 2000 to worker 4
so let’s split the minimum and maximum values according to the number of workers
"""
nrange = nmax - nmin
threads = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
# Start the thread with the minimum and maximum split up to compute
# Parallel computation will not work here due to the GIL since this is a CPU-bound task
t = threading.Thread(target = find_primes_in, args = (start, end))
threads.append(t)
t.start()
# Don’t forget to wait for the threads to finish
for t in threads:
t.join()
@time_stuff
def processing_prime_finder(nmin, nmax):
"""
Split the minimum, maximum interval similar to the threading method above, but use processes this time
"""
nrange = nmax - nmin
processes = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
p = multiprocessing.Process(target = find_primes_in, args = (start, end))
processes.append(p)
p.start()
for p in processes:
p.join()
@time_stuff
def thread_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use a thread pool executor this time.
This method is slightly faster than using pure threading as the pools manage threads more efficiently.
This method is still slow due to the GIL limitations since we are doing a CPU-bound task.
"""
nrange = nmax - nmin
with ThreadPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
@time_stuff
def process_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use the process pool executor.
This is the fastest method recorded so far as it manages process efficiently + overcomes GIL limitations.
RECOMMENDED METHOD FOR CPU-BOUND TASKS
"""
nrange = nmax - nmin
with ProcessPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
def main():
nmin = int(1e7)
nmax = int(1.05e7)
print("Sequential Prime Finder Starting")
sequential_prime_finder(nmin, nmax)
print("Threading Prime Finder Starting")
threading_prime_finder(nmin, nmax)
print("Processing Prime Finder Starting")
processing_prime_finder(nmin, nmax)
print("Thread Executor Prime Finder Starting")
thread_executor_prime_finder(nmin, nmax)
print("Process Executor Finder Starting")
process_executor_prime_finder(nmin, nmax)
main()
Mac OS X 4 코어 컴퓨터의 결과는 다음과 같습니다.
Sequential Prime Finder Starting
9.708213827005238 seconds
Threading Prime Finder Starting
9.81836523200036 seconds
Processing Prime Finder Starting
3.2467174359990167 seconds
Thread Executor Prime Finder Starting
10.228896902000997 seconds
Process Executor Finder Starting
2.656402041000547 seconds
if __name__ == '__main__':
는 주 호출 이전에 필요합니다 . 그렇지 않으면 측정이 생성되고 인쇄 됩니다. 전에 새 프로세스를 시작하려고 시도했습니다 .
다음은 스레딩을 사용한 CSV 가져 오기 의 매우 간단한 예입니다 . (라이브러리 포함은 목적에 따라 다를 수 있습니다.)
도우미 기능 :
from threading import Thread
from project import app
import csv
def import_handler(csv_file_name):
thr = Thread(target=dump_async_csv_data, args=[csv_file_name])
thr.start()
def dump_async_csv_data(csv_file_name):
with app.app_context():
with open(csv_file_name) as File:
reader = csv.DictReader(File)
for row in reader:
# DB operation/query
운전사 기능 :
import_handler(csv_file_name)
간단한 예제와 함께이 문제를 직접 해결해야 할 때 유용한 설명을 제공하고자합니다.
이 답변에는 Python의 GIL (전역 인터프리터 잠금) 에 대한 정보 와 multiprocessing.dummy를 사용하여 작성된 간단한 예제 및 간단한 벤치 마크가 있습니다.
글로벌 통역사 잠금 (GIL)
파이썬은 단어의 진정한 의미에서 멀티 스레딩을 허용하지 않습니다. 멀티 스레딩 패키지가 있지만 코드 속도를 높이기 위해 멀티 스레드를 원할 경우 일반적으로 사용하지 않는 것이 좋습니다.
파이썬에는 GIL (Global Interpreter Lock)이라는 구조가 있습니다. GIL은 한 번에 하나의 '스레드'만 실행할 수 있도록합니다. 스레드는 GIL을 획득하고 약간의 작업을 수행 한 다음 GIL을 다음 스레드로 전달합니다.
이것은 사람의 눈에 매우 빠르게 발생하므로 스레드가 병렬로 실행되는 것처럼 보일 수 있지만 실제로는 동일한 CPU 코어를 사용하여 교대로 진행됩니다.
이 모든 GIL 전달은 실행에 오버 헤드를 추가합니다. 즉, 코드를 빠르게 실행하려면 스레딩 패키지를 사용하는 것이 좋지 않습니다.
파이썬의 스레딩 패키지를 사용해야하는 이유가 있습니다. 동시에 몇 가지 작업을 수행하고 효율성이 중요하지 않은 경우 완전히 훌륭하고 편리합니다. 또는 (일부 I / O와 같은) 무언가를 기다려야하는 코드를 실행하는 경우 많은 의미가있을 수 있습니다. 그러나 스레딩 라이브러리에서는 추가 CPU 코어를 사용할 수 없습니다.
멀티 스레딩은 멀티 프로세싱을 통해 운영 체제로 아웃소싱 할 수 있으며 Python 코드를 호출하는 일부 외부 응용 프로그램 (예 : Spark 또는 Hadoop) ) Python 코드가 호출하는 일부 코드 (예 : 파이썬 코드가 비싼 멀티 스레드 작업을 수행하는 C 함수를 호출하도록하십시오).
이것이 중요한 이유
많은 사람들이 GIL이 무엇인지 배우기 전에 멋진 Python 멀티 스레드 코드에서 병목 현상을 찾으려고 많은 시간을 소비하기 때문입니다.
이 정보가 명확 해지면 내 코드는 다음과 같습니다.
#!/bin/python
from multiprocessing.dummy import Pool
from subprocess import PIPE,Popen
import time
import os
# In the variable pool_size we define the "parallelness".
# For CPU-bound tasks, it doesn't make sense to create more Pool processes
# than you have cores to run them on.
#
# On the other hand, if you are using I/O-bound tasks, it may make sense
# to create a quite a few more Pool processes than cores, since the processes
# will probably spend most their time blocked (waiting for I/O to complete).
pool_size = 8
def do_ping(ip):
if os.name == 'nt':
print ("Using Windows Ping to " + ip)
proc = Popen(['ping', ip], stdout=PIPE)
return proc.communicate()[0]
else:
print ("Using Linux / Unix Ping to " + ip)
proc = Popen(['ping', ip, '-c', '4'], stdout=PIPE)
return proc.communicate()[0]
os.system('cls' if os.name=='nt' else 'clear')
print ("Running using threads\n")
start_time = time.time()
pool = Pool(pool_size)
website_names = ["www.google.com","www.facebook.com","www.pinterest.com","www.microsoft.com"]
result = {}
for website_name in website_names:
result[website_name] = pool.apply_async(do_ping, args=(website_name,))
pool.close()
pool.join()
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Now we do the same without threading, just to compare time
print ("\nRunning NOT using threads\n")
start_time = time.time()
for website_name in website_names:
do_ping(website_name)
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Here's one way to print the final output from the threads
output = {}
for key, value in result.items():
output[key] = value.get()
print ("\nOutput aggregated in a Dictionary:")
print (output)
print ("\n")
print ("\nPretty printed output: ")
for key, value in output.items():
print (key + "\n")
print (value)
다음은 간단한 예제가있는 멀티 스레딩입니다. 파이썬에서 멀티 스레딩이 어떻게 작동하는지 쉽게 이해할 수 있습니다. 이전 스레드가 작업을 완료 할 때까지 다른 스레드에 대한 액세스를 방지하기 위해 잠금을 사용했습니다. 이 코드 줄을 사용하면
tLock = 스레딩. 경계 세마포어 (값 = 4)
한 번에 여러 프로세스를 허용하고 나중에 또는 이전 프로세스가 완료된 후에 실행될 나머지 스레드를 유지할 수 있습니다.
import threading
import time
#tLock = threading.Lock()
tLock = threading.BoundedSemaphore(value=4)
def timer(name, delay, repeat):
print "\r\nTimer: ", name, " Started"
tLock.acquire()
print "\r\n", name, " has the acquired the lock"
while repeat > 0:
time.sleep(delay)
print "\r\n", name, ": ", str(time.ctime(time.time()))
repeat -= 1
print "\r\n", name, " is releaseing the lock"
tLock.release()
print "\r\nTimer: ", name, " Completed"
def Main():
t1 = threading.Thread(target=timer, args=("Timer1", 2, 5))
t2 = threading.Thread(target=timer, args=("Timer2", 3, 5))
t3 = threading.Thread(target=timer, args=("Timer3", 4, 5))
t4 = threading.Thread(target=timer, args=("Timer4", 5, 5))
t5 = threading.Thread(target=timer, args=("Timer5", 0.1, 5))
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
print "\r\nMain Complete"
if __name__ == "__main__":
Main()
이 게시물 에서 빌리면서 우리는 멀티 스레딩, 멀티 프로세싱 및 async /asyncio
와 그 사용법 있습니다.
Python 3 에는 동시성 및 병렬 처리를 위해 새로운 내장 라이브러리가 있습니다. 동시.
따라서 실험을 통해 네 가지 작업 (예 : .sleep()
방법)을 다음 Threading-Pool
과 같이 실행합니다 .
from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep, time
def concurrent(max_worker=1):
futures = []
tick = time()
with ThreadPoolExecutor(max_workers=max_worker) as executor:
futures.append(executor.submit(sleep, 2)) # Two seconds sleep
futures.append(executor.submit(sleep, 1))
futures.append(executor.submit(sleep, 7))
futures.append(executor.submit(sleep, 3))
for future in as_completed(futures):
if future.result() is not None:
print(future.result())
print('Total elapsed time by {} workers:'.format(max_worker), time()-tick)
concurrent(5)
concurrent(4)
concurrent(3)
concurrent(2)
concurrent(1)
산출:
Total elapsed time by 5 workers: 7.007831811904907
Total elapsed time by 4 workers: 7.007944107055664
Total elapsed time by 3 workers: 7.003149509429932
Total elapsed time by 2 workers: 8.004627466201782
Total elapsed time by 1 workers: 13.013478994369507
[ 참고 ] :
multiprocessing
대 threading
당신을 바꿀 수) ThreadPoolExecutor
에 ProcessPoolExecutor
.이전 솔루션 중 실제로 GNU / Linux 서버 (관리자 권한이없는)에서 여러 코어를 사용한 적이 없습니다. 그들은 방금 단일 코어에서 달렸습니다.
저수준 os.fork
인터페이스를 사용하여 여러 프로세스를 생성했습니다. 이것은 나를 위해 일한 코드입니다.
from os import fork
values = ['different', 'values', 'for', 'threads']
for i in range(len(values)):
p = fork()
if p == 0:
my_function(values[i])
break
import threading
import requests
def send():
r = requests.get('https://www.stackoverlow.com')
thread = []
t = threading.Thread(target=send())
thread.append(t)
t.start()