解决多线程无返回值的问题

本文介绍了如何在Python 2.7的线程实现中,自定义ThreadResult类,以便获取线程执行函数的结果。作者展示了如何重写run方法,以及如何在函数参数中传递全局变量存储结果。

查看线程的源码内容(threading.py)python2.7的版本:

def run(self):
    """Method representing the thread's activity.

    You may override this method in a subclass. The standard run() method
    invokes the callable object passed to the object's constructor as the
    target argument, if any, with sequential and keyword arguments taken
    from the args and kwargs arguments, respectively.

    """
    try:
        if self.__target:
            self.__target(*self.__args, **self.__kwargs)
    finally:
        # Avoid a refcycle if the thread is running a function with
        # an argument that has a member that points to the thread.
        del self.__target, self.__args, self.__kwargs

可以看到,线程在start之后,只是运行了target的方法,并没有返回target方法的返回值,所以,我们需要重写线程的run函数。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading


class ThreadResult(threading.Thread):
    def __init__(self, func, args=()):
        super(ThreadResult, self).__init__()
        self.func = func
        self.args = args
        self.result = None

    def run(self):
        self.result = self.func(*self.args)

def a(n):
    return n + 1

def main(ips):
    pp = [1,2,3,4,5]
    ths = list()
    for p in pp:
        t = ThreadResult(func=a, args=(p,))
        ths.append(t)
        t.start()

    t_results = list()
    for h in ths:
        h.join()
        t_results.append(h.result)
    return t_results

还有个方法是:在a函数的参数中,再传一个全局变量列表,用于存储本次调用a函数的执行结果。

<think> 在Java中,当使用多线程并需要获取返回值时,我们需要能够区分每个线程的返回结果。常用的方法是通过线程池提交任务(如使用`ExecutorService`的`submit`方法)并获取`Future`对象,然后通过`Future`对象获取对应任务的返回值。每个`Future`对象与提交的任务一一对应,因此可以通过不同的`Future`对象获取不同线程返回值。 ### 解决方案 1. **使用`Callable`和`Future`**:为每个任务实现`Callable`接口,它允许返回结果。提交任务到线程池后会返回一个`Future`对象,通过该对象可以获取对应任务的返回值。 2. **使用`CompletionService`**:如果我们需要按照任务完成的顺序获取结果,可以使用`CompletionService`,它组合了`ExecutorService`和`BlockingQueue`,可以获取已完成任务的结果。 ### 示例代码 #### 方法1:使用`Future`获取特定任务的返回值 ```java import java.util.concurrent.*; public class FutureExample { public static void main(String[] args) throws Exception { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(2); // 提交两个Callable任务,并保存Future对象 Future<String> future1 = executor.submit(new MyCallable("Task1", 2000)); Future<String> future2 = executor.submit(new MyCallable("Task2", 1000)); // 通过Future对象获取返回值(会阻塞直到任务完成) String result1 = future1.get(); // 对应Task1的返回值 String result2 = future2.get(); // 对应Task2的返回值 System.out.println("Task1 result: " + result1); System.out.println("Task2 result: " + result2); executor.shutdown(); } } class MyCallable implements Callable<String> { private String name; private long sleepTime; public MyCallable(String name, long sleepTime) { this.name = name; this.sleepTime = sleepTime; } @Override public String call() throws Exception { Thread.sleep(sleepTime); return "Result from " + name + " executed by " + Thread.currentThread().getName(); } } ``` #### 方法2:使用`CompletionService`按完成顺序获取结果 ```java import java.util.concurrent.*; public class CompletionServiceExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(3); CompletionService<String> completionService = new ExecutorCompletionService<>(executor); // 提交3个任务 completionService.submit(new MyCallable("Task1", 3000)); completionService.submit(new MyCallable("Task2", 1000)); completionService.submit(new MyCallable("Task3", 2000)); // 按完成顺序获取结果 for (int i = 0; i < 3; i++) { Future<String> future = completionService.take(); // 获取下一个已完成任务的Future String result = future.get(); // 获取返回值 System.out.println("Completed: " + result); } executor.shutdown(); } } ``` ### 关键点解释 1. **`Future`对象与任务绑定**:每次调用`executor.submit()`会返回一个`Future`对象,该对象与传入的`Callable`(或`Runnable`)任务一一对应。因此,通过保存不同的`Future`对象,可以区分不同任务的返回值。 2. **`CompletionService`的作用**:它允许我们按照任务完成的先后顺序处理结果,而不是按照提交顺序。内部使用一个阻塞队列来保存已完成任务的结果(`Future`对象)。 3. **阻塞与非阻塞**: - `future.get()`会阻塞直到任务完成。 - 可以使用`future.isDone()`检查任务是否完成,避免阻塞。 ### 如何确定返回值属于哪个线程? - 在`Callable`的实现中,可以返回包含线程标识(如线程名称)的字符串,如示例中的`"Result from " + name + " executed by " + Thread.currentThread().getName()`。 - 在获取结果时,通过`Future`对象与任务的关系,我们可以知道该结果属于哪个任务(因为我们在提交任务时保存了对应的`Future`)。 ### 注意事项 1. **任务标识**:如果需要在结果中区分任务,可以在任务内部添加标识(如任务名称),并返回包含该标识的结果。 2. **异常处理**:调用`future.get()`可能会抛出`ExecutionException`,表示任务执行过程中抛出了异常,需要进行捕获和处理。 3. **超时**:`future.get(long timeout, TimeUnit unit)`可以设置超时时间,避免长时间阻塞。 §§相关问题§§: 1. Java中Callable和Runnable接口有什么区别? 2. Future.get()方法会阻塞当前线程吗?如何避免阻塞? 3. CompletionService和ExecutorService在获取任务结果时有什么不同? 4. 如何处理多线程任务执行过程中抛出的异常? </think>在Java多线程中确定返回值属于哪个线程,可以通过以下方法实现: ### 解决方案及代码示例 #### 1. 使用`Callable`和`Future`组合(最常用) ```java import java.util.concurrent.*; public class ThreadReturnExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(3); // 创建多个Callable任务(每个任务有唯一标识) Callable<String> task1 = () -> { Thread.sleep(1000); return "Result from " + Thread.currentThread().getName() + " (Task1)"; }; Callable<String> task2 = () -> { Thread.sleep(500); return "Result from " + Thread.currentThread().getName() + " (Task2)"; }; // 提交任务并获取Future对象 Future<String> future1 = executor.submit(task1); Future<String> future2 = executor.submit(task2); // 通过Future对象获取结果并识别来源 System.out.println("Task1 result: " + future1.get()); System.out.println("Task2 result: " + future2.get()); executor.shutdown(); } } ``` #### 2. 使用`CompletionService`(按完成顺序处理) ```java import java.util.concurrent.*; public class CompletionServiceExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(3); CompletionService<String> completionService = new ExecutorCompletionService<>(executor); // 提交带标识的任务 completionService.submit(() -> "Task-1 result from " + Thread.currentThread().getName()); completionService.submit(() -> "Task-2 result from " + Thread.currentThread().getName()); // 按完成顺序获取结果 Future<String> future; for (int i = 0; i < 2; i++) { future = completionService.take(); // 阻塞直到有任务完成 System.out.println("Completed: " + future.get()); } executor.shutdown(); } } ``` #### 3. 使用线程局部变量(ThreadLocal) ```java import java.util.concurrent.*; public class ThreadLocalExample { // 定义线程局部变量存储结果 static final ThreadLocal<String> threadResult = new ThreadLocal<>(); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); Runnable task = () -> { String result = "Result from " + Thread.currentThread().getName(); threadResult.set(result); // 设置线程私有结果 System.out.println(threadResult.get()); // 获取当前线程的结果 }; executor.execute(task); executor.execute(task); executor.shutdown(); } } ``` ### 关键原理说明 1. **Future标识机制**: - 每个`Future`对象与提交的`Callable`任务一一对应 - `Future.get()`返回的即是该特定任务的执行结果 - 通过保存不同任务的`Future`引用,可精确区分结果来源 2. **线程名称标识**: ```java // 在任务结果中包含线程名称 return "TaskID-123@" + Thread.currentThread().getName(); ``` - 通过`Thread.currentThread().getName()`获取执行线程的名称 - 可在结果字符串中加入任务ID或线程名进行标识 3. **ThreadLocal原理**: - 每个线程有独立的变量副本 - 通过`threadLocal.get()`获取当前线程设置的值 - 适用于需要在任务执行过程中传递上下文 ### 常见问题解决 **问题场景**:线程池中线程复用导致ThreadLocal值混乱 **解决方案**: ```java executor.execute(() -> { try { threadResult.set("Value"); // ... 业务逻辑 ... } finally { threadResult.remove(); // 必须清理防止污染后续任务 } }); ``` **问题场景**:需要获取全部任务的返回值 **解决方案**: ```java List<Future<String>> futures = new ArrayList<>(); futures.add(executor.submit(task1)); futures.add(executor.submit(task2)); for (Future<String> future : futures) { System.out.println("Task result: " + future.get()); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值