杰瑞科技汇

python thread(target

Of course! Let's break down the threading.Thread(target=...) constructor in Python, which is the core of creating and managing threads.

python thread(target-图1
(图片来源网络,侵删)

The Short Answer

threading.Thread(target=my_function) creates a new thread that, when started, will execute the function my_function. The target argument simply points to the function you want the thread to run.


In-Depth Explanation with Examples

To fully understand target, let's look at the anatomy of a simple threaded program.

The Basic Concept: target and args

When you create a Thread object, you need to tell it two main things:

  • What to do: This is the target.
  • What to pass to it: This is the args (a tuple of arguments).

Here's a classic "Hello World" for threading: running two functions concurrently.

python thread(target-图2
(图片来源网络,侵删)
import threading
import time
# This is the function that will be run by the thread
def print_numbers(limit):
    """Prints numbers from 1 to a given limit."""
    for i in range(1, limit + 1):
        print(f"Number: {i}")
        time.sleep(0.5) # Simulate a task that takes time
def print_letters(limit):
    """Prints letters from A to a given limit."""
    for letter in range(ord('A'), ord('A') + limit):
        print(f"Letter: {chr(letter)}")
        time.sleep(0.5) # Simulate a task that takes time
# --- Main part of the script ---
if __name__ == "__main__":
    # Create the thread objects
    # 1. target is the function to run
    # 2. args is a tuple of arguments for the target function
    thread1 = threading.Thread(target=print_numbers, args=(5,))
    thread2 = threading.Thread(target=print_letters, args=(5,))
    print("Starting threads...")
    # Start the threads (this begins their execution)
    thread1.start()
    thread2.start()
    # Wait for both threads to complete their execution before the main program continues
    thread1.join()
    thread2.join()
    print("All threads finished.")

How it works:

  1. We define two functions, print_numbers and print_letters.
  2. We create two Thread objects.
    • For thread1, target=print_numbers tells it to run the print_numbers function.
    • args=(5,) passes the integer 5 as an argument to print_numbers. Note the comma! args must be a tuple, so (5,) is a tuple with one element, while (5) is just the number 5.
  3. thread1.start() and thread2.start() tell the Python interpreter to begin executing the target functions in new threads. At this point, the main program continues immediately.
  4. thread1.join() and thread2.join() are crucial. They pause the main program until each respective thread has finished its work. Without them, the main script might print "All threads finished." before the threads are done.

What if the Function Needs No Arguments?

If your target function doesn't require any arguments, you can simply omit the args parameter.

import threading
import time
def simple_task():
    """A function that takes no arguments."""
    print("Starting simple task...")
    time.sleep(2)
    print("Simple task finished.")
if __name__ == "__main__":
    # No args needed
    thread = threading.Thread(target=simple_task)
    thread.start()
    thread.join()
    print("Main program finished.")

What if the Function Needs Keyword Arguments?

You can also pass keyword arguments using the kwargs parameter. kwargs must be a dictionary.

import threading
def greet(name, greeting="Hello"):
    """A function that takes positional and keyword arguments."""
    print(f"{greeting}, {name}!")
if __name__ == "__main__":
    # Pass positional args and keyword args
    thread = threading.Thread(
        target=greet,
        args=("Alice",),  # Positional argument for 'name'
        kwargs={"greeting": "Hi"} # Keyword argument for 'greeting'
    )
    thread.start()
    thread.join()
    # Expected output:
    # Hi, Alice!

Key Considerations and Best Practices

The Global Interpreter Lock (GIL)

In CPython (the standard Python implementation), the GIL is a mutex that protects access to Python objects, preventing multiple native threads from executing Python bytecode at the same time within a single process.

python thread(target-图3
(图片来源网络,侵删)
  • What this means for CPU-bound tasks: For tasks that are heavy on computation (e.g., mathematical calculations, data processing), multithreading in Python does not provide true parallelism. The GIL ensures that only one thread runs at a time, switching between them rapidly.
  • What this means for I/O-bound tasks: For tasks that spend most of their time waiting for external operations (e.g., reading/writing files, making network requests), multithreading is highly effective. While one thread is waiting for I/O, the GIL is released, allowing another thread to run.

Conclusion: Use threading for I/O-bound tasks. For CPU-bound tasks, use the multiprocessing module, which creates separate processes and bypasses the GIL.

Sharing Data Between Threads (A Common Pitfall)

Threads share the same memory space. This is powerful but dangerous if not handled correctly, as it can lead to race conditions.

import threading
# A shared variable
shared_counter = 0
def increment_counter():
    global shared_counter
    for _ in range(1_000_000):
        shared_counter += 1 # This is NOT an atomic operation!
if __name__ == "__main__":
    threads = []
    for _ in range(2):
        thread = threading.Thread(target=increment_counter)
        threads.append(thread)
        thread.start()
    for thread in threads:
        thread.join()
    print(f"Expected counter: 2,000,000")
    print(f"Actual counter:   {shared_counter}") # Likely not 2,000,000!

The output will almost certainly be less than 2,000,000. This is because shared_counter += 1 involves three steps: read, modify, write. If two threads interleave these steps, they can overwrite each other's changes.

The Solution: Use Lock to ensure only one thread can modify the shared data at a time.

import threading
shared_counter = 0
# Create a Lock object
counter_lock = threading.Lock()
def increment_counter():
    global shared_counter
    for _ in range(1_000_000):
        with counter_lock: # Acquire the lock before entering the critical section
            shared_counter += 1
if __name__ == "__main__":
    threads = []
    for _ in range(2):
        thread = threading.Thread(target=increment_counter)
        threads.append(thread)
        thread.start()
    for thread in threads:
        thread.join()
    print(f"Expected counter: 2,000,000")
    print(f"Actual counter:   {shared_counter}") # Correctly 2,000,000

The with counter_lock: statement ensures that only one thread can execute the code block inside at a time, preventing race conditions.

Summary: target vs. args vs. kwargs

Argument Purpose Type Example
target The function to be executed by the thread. A callable function. target=my_function
args A tuple of positional arguments to pass to the target function. A tuple. args=(10, "hello")
kwargs A dictionary of keyword arguments to pass to the target function. A dict. kwargs={"name": "Bob", "age": 30}
分享:
扫描分享到社交APP
上一篇
下一篇