hàm nền trong Python


88

Tôi có một tập lệnh Python đôi khi hiển thị hình ảnh cho người dùng. Đôi khi, hình ảnh có thể khá lớn và chúng được sử dụng lại thường xuyên. Hiển thị chúng không phải là điều quan trọng, nhưng hiển thị thông điệp liên quan đến chúng mới là quan trọng. Tôi có một chức năng tải xuống hình ảnh cần thiết và lưu nó cục bộ. Ngay bây giờ, nó chạy nội tuyến với mã hiển thị thông báo cho người dùng, nhưng đôi khi có thể mất hơn 10 giây đối với các hình ảnh không cục bộ. Có cách nào tôi có thể gọi hàm này khi cần, nhưng chạy nó ở chế độ nền trong khi mã tiếp tục thực thi không? Tôi sẽ chỉ sử dụng một hình ảnh mặc định cho đến khi hình ảnh chính xác có sẵn.

Câu trả lời:


129

Làm điều gì đó như sau:

def function_that_downloads(my_args):
    # do some long download here

sau đó nội tuyến, làm một cái gì đó như sau:

import threading
def my_inline_function(some_args):
    # do some stuff
    download_thread = threading.Thread(target=function_that_downloads, name="Downloader", args=some_args)
    download_thread.start()
    # continue doing stuff

Bạn có thể muốn kiểm tra xem chuỗi đã hoàn thành chưa trước khi chuyển sang những thứ khác bằng cách gọi download_thread.isAlive()


Trình thông dịch vẫn mở cho đến khi chuỗi đóng lại. (ví dụ import threading, time; wait=lambda: time.sleep(2); t=threading.Thread(target=wait); t.start(); print('end')). Tôi đã hy vọng "lý lịch" ngụ ý cũng được tách ra.
ThorSummoner,

3
@ThorSummoner Chủ đề đều được chứa trong cùng một quy trình. Nếu bạn đang muốn tạo ra một quy trình mới, bạn sẽ muốn xem xét các mô-đun subprocesshoặc multiprocessingpython thay thế.
TorelTwiddler

@TorelTwiddler Tôi muốn chạy một hàm trong nền nhưng tôi gặp một số hạn chế về tài nguyên và không thể chạy hàm nhiều lần mà tôi muốn và muốn xếp hàng đợi các lần thực thi bổ sung của hàm. Bạn có bất kỳ ý tưởng nào về cách tôi nên làm điều đó? Tôi có câu hỏi của tôi ở đây . Bạn có thể vui lòng xem qua câu hỏi của tôi? Bất kỳ sự trợ giúp nào đều sẽ là tuyệt vời!
Amir

3
Nếu bạn muốn nhiều tham số: download_thread = threading.Thread (target = function_that_downloads, args = (variable1, variable2, variableN))
georgeos

làm thế nào để vượt qua nhiều args - như phương pháp này là add(a,b)và nhận giá trị trả về của phương pháp đó
Maifee Ul Asad

7

Thông thường, cách thực hiện điều này sẽ là sử dụng nhóm luồng và hàng đợi tải xuống sẽ đưa ra một tín hiệu, hay còn gọi là sự kiện, khi tác vụ đó hoàn tất xử lý. Bạn có thể làm điều này trong phạm vi mô-đun phân luồng mà Python cung cấp.

Để thực hiện các hành động đã nói, tôi sẽ sử dụng các đối tượng sự kiệnmô-đun Hàng đợi .

Tuy nhiên, có thể thấy một minh chứng nhanh chóng và rõ ràng về những gì bạn có thể làm bằng cách threading.Threadtriển khai đơn giản dưới đây:

import os
import threading
import time
import urllib2


class ImageDownloader(threading.Thread):

    def __init__(self, function_that_downloads):
        threading.Thread.__init__(self)
        self.runnable = function_that_downloads
        self.daemon = True

    def run(self):
        self.runnable()


def downloads():
    with open('somefile.html', 'w+') as f:
        try:
            f.write(urllib2.urlopen('http://google.com').read())
        except urllib2.HTTPError:
            f.write('sorry no dice')


print 'hi there user'
print 'how are you today?'
thread = ImageDownloader(downloads)
thread.start()
while not os.path.exists('somefile.html'):
    print 'i am executing but the thread has started to download'
    time.sleep(1)

print 'look ma, thread is not alive: ', thread.is_alive()

Có lẽ sẽ có ý nghĩa nếu không thăm dò ý kiến ​​như tôi đang làm ở trên. Trong trường hợp đó, tôi sẽ thay đổi mã thành này:

import os
import threading
import time
import urllib2


class ImageDownloader(threading.Thread):

    def __init__(self, function_that_downloads):
        threading.Thread.__init__(self)
        self.runnable = function_that_downloads

    def run(self):
        self.runnable()


def downloads():
    with open('somefile.html', 'w+') as f:
        try:
            f.write(urllib2.urlopen('http://google.com').read())
        except urllib2.HTTPError:
            f.write('sorry no dice')


print 'hi there user'
print 'how are you today?'
thread = ImageDownloader(downloads)
thread.start()
# show message
thread.join()
# display image

Lưu ý rằng không có cờ daemon nào được đặt ở đây.


4

Tôi thích sử dụng gevent cho những việc này:

import gevent
from gevent import monkey; monkey.patch_all()

greenlet = gevent.spawn( function_to_download_image )
display_message()
# ... perhaps interaction with the user here

# this will wait for the operation to complete (optional)
greenlet.join()
# alternatively if the image display is no longer important, this will abort it:
#greenlet.kill()

Mọi thứ đều chạy trong một luồng, nhưng bất cứ khi nào khối hoạt động của hạt nhân khối, gevent sẽ chuyển đổi ngữ cảnh khi có các "greenlet" khác đang chạy. Lo lắng về việc khóa, v.v. đã giảm đi nhiều, vì chỉ có một thứ đang chạy tại một thời điểm, nhưng hình ảnh sẽ tiếp tục tải xuống bất cứ khi nào hoạt động chặn thực thi trong ngữ cảnh "chính".

Tùy thuộc vào mức độ và loại điều bạn muốn làm trong nền, điều này có thể tốt hơn hoặc tệ hơn so với các giải pháp dựa trên luồng; chắc chắn, nó có thể mở rộng hơn nhiều (tức là bạn có thể làm nhiều thứ khác trong nền), nhưng điều đó có thể không đáng lo ngại trong tình hình hiện tại.


mục đích của dòng này là from gevent import monkey; monkey.patch_all()gì:?
nz_21

bản vá thư viện tiêu chuẩn để tương thích với gevent
gevent.org/api/gevent.monkey.html
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.