【问题标题】:Sharing object context Tasks共享对象上下文任务
【发布时间】:2017-03-24 08:43:58
【问题描述】:

我有一个列表 lstSubscriptionRequests 我正在对哪些单独的项目进行某种异步处理。然后在处理完所有项目后,我必须返回更新的列表项目。我目前的实现是这样的

List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource();

 var tsk = new List<Task>();
                foreach (var oTsk in lstSubscriptionRequests.Select(objSubscriptionRequest => new Task(
                    () => ProcessSubscriptionForASingleRecord(objSubscriptionRequest))))
                {
                    oTsk.Start();
                    lock (tsk)
                    {
                        tsk.Add(oTsk);
                    }
                }
                Task.WaitAll(tsk.ToArray());

所有任务完成后的某些项目似乎没有更新。 请让我知道我需要什么更正

【问题讨论】:

  • 不要自己创建任务,使用awaitTask.Run。您的任务根本没有开始
  • 你能帮我改正上面的代码吗?
  • 你为什么要创建这样的冷任务,为什么你在主线程仍然时使用lock?您的代码也不包含Parallel.ForEach,也不包含任何共享状态的尝试。不可能这样回答。例如Parallel.ForEach(lstSubscriptionRequests,ProcessSubscriptionForASingleRecord) 等价于这段代码。你为什么不使用它?如果ProcessSubscription.. 产生结果使用PLINQ,例如from subscription in lstSubscriptionRequests.AsParallel() select ProcessSubscriptionForASingleRecord(subscription)
  • 请说明您想做什么,而不是您如何尝试。
  • 我有一个 Parallel.For 每个我在其中开始任务。现在我把它删除了

标签: c# multithreading thread-safety task-parallel-library .net-4.5.2


【解决方案1】:

您可以更轻松地完成此任务。请注意,通常不需要或不推荐使用 Task 构造函数,而且更改特定对象的状态也可能难以跟踪或调试。返回代表您所需状态的新对象将允许您强制执行最小有效状态。以下代码将处理您的所有项目并将完成的项目返回给您的客户端代码。

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Processing {
    public class MyProcessor {

        public async Task<IEnumerable<Subscription>> ProcessSubscriptionRequestsAsync(IEnumerable<SubscriptionRequest> subscriptionRequests) {
            var subscriptionProcessingTasks = subscriptionRequests.Select(request => ProcessSubscriptionForASingleRecord(request)).ToArray();
            return await Task.WhenAll(subscriptionProcessingTasks);
        }

        public async Task<Subscription> ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
            //process the request
            try {
                var subscription = await Context.ProcessRequest(request);
                return subscription;
            } catch {
                //something went wrong with the request
            }
        }
    }

    public class SubscriptionRequest {
        //A subcription request
    }

    public class Subscription {
        //A completed subscription request
    }
}

更新

如果您可以排除新的课程订阅并在您的答案中添加解决方案,这可能会有所帮助。那我试试看

希望简化的前后视图更易于集成。主要区别是将Parallel.ForEach 替换为Select 以创建您的任务集合,而无需为每个SubscriptionRequest 锁定任务列表,通常也不需要并行处理Tasks因为每一个都将异步执行,所以您只会更快地达到所有等待,而不是完成的地步。接下来每个任务都被允许开始,所有任务都在await Task.WhenAll(tasks) 等待。确定每个SubscriptionRequest 经历的处理类型对您来说很重要。为了举例,我假设每个请求都以某种方式链接到数据库访问,即存储请求、更新各种用户配置文件等。

public class OriginalSynchronous {

    public void ProcessSubscriptionRequest() {
        List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource();

        List<Task> tsk = new List<Task>();
        Parallel.ForEach(lstSubscriptionRequests, objSubscriptionRequest => {
            var oTsk =
                new Task(
                    () => ProcessSubscriptionForASingleRecord(objSubscriptionRequest));// update some properties after processing SubscriptionRequest
            oTsk.Start();
            lock (tsk) {
                tsk.Add(oTsk);
            }

        });
        Task.WaitAll(tsk.ToArray());
    }

    private void ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
        //modify SubscriptionRequest
    }
}

public class ModifiedAsync {

    public async Task ProcessSubscriptionRequest() {
        var subscriptionRequests = await FromSomeResourceAsync();
        var tasks = subscriptionRequests.Select(request => {
            return ProcessSubscriptionForASingleRecord(request);
        }).ToArray();
        await Task.WhenAll(tasks);
    }

    public async Task ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
        //modify SubscriptionRequest
    }
}

【讨论】:

  • 不明白为什么我要订阅另一个课程?
  • @KamranShahid 你没有ProcessSubscriptionForASingleRecord 也可以像你的原始代码一样修改SubscrptionRequest。这样做将允许您删除手动创建Task、不必要的lock,以及导致原始代码出现问题的不必要的Parallel.ForEach。添加Subscription 只是对进一步完善代码的建议,并非严格要求。
  • 如果您可以排除新的课程订阅并在您的答案中添加解决方案,这可能会有所帮助。那我试试看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 2013-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-27
相关资源
最近更新 更多