【问题标题】:Alternatives to using an asynchronous method in .net 4.0在 .net 4.0 中使用异步方法的替代方法
【发布时间】:2013-09-19 15:02:55
【问题描述】:

我正在编写一个 WCF 服务,它必须实现多线程才能调用另一个 3rd 方服务。

我正在使用 LINQ 创建一个“列表”并将对象列表发送到客户端。

“MyObject”有 5 个属性:

  • 3 静态存储在一个表中
  • 2 由 3rd 方服务动态计算。

我需要生成线程来读取 2 个动态属性,超时时间为 30 秒。示例:

如果列表中有6个对象,

  • 我应该生成 6 个并行异步执行的线程,它们为它们的 2 个属性中的每一个调用 3rd 方服务。
  • 如果我在 30 秒内获得所有结果,我会将这 6 个对象连同所有属性一起发送给客户端。
  • 如果我在 30 秒内没有收到任何对象的属性,我必须将该对象的属性设置为 NULL,并将对象列表返回给客户端。

我的代码如下所示:

List<Dials> lstDial = new List<Dials>();
foreach(var c in dialsToShow)
{
    int threadId;
    AsyncMethodCaller caller =
        new AsyncMethodCaller(MyMethodThatCalls3rdPartyService);

    IAsyncResult result = caller.BeginInvoke(userName, out threadId, null, null);

    while (!result.AsyncWaitHandle.WaitOne(30000, false))
         avgBI = caller.EndInvoke(out threadId, result);

    property4 = avgBI[0];
    property5= avgBI[1]; 

    var dashboardObj = _repository.FindQueryable<DashboardDial_Tbl>()
                   .Select(p => new DialDetails()
                   {
                       Id = p.DialId,
                       Name= p.DialName,
                       Type = p.DialType,
                       IndividualStat = property4,
                       GroupStat = proprty5         
                    }).SingleOrDefault();
      dials.Add(dashboardObj); 
}
reponse.dialsList = dials;

调用 3rd 方服务的方法有一个委托。它看起来像这样:

public delegate List<string> AsyncMethodCaller(
    string methodName, out int threadId);

private List<string> MyMethodThatCalls3rdPartyService(
    string userName, out int threadId)
{
    //Call the service
}

有人可以帮助我以另一种方式使用多线程来实现这一点吗?我正在使用 .Net 4.0 和 VS2012。

【问题讨论】:

  • 看看任务库,简单多了
  • 你能帮我一些伪代码吗?
  • 我将重新表述你的问题,因为这不是专门针对 WCF 的

标签: .net multithreading c#-4.0 asynchronous


【解决方案1】:

您可以使用Parallel 并行运行并发任务:

public void DoStuff()
{
    var input = new[] {2, 3, 4};

    var results = new ConcurrentBag<string>();

    Parallel.ForEach(input, value =>
        {
            value += 10;
            results.Add(value.ToString());
        });

    foreach (var result in results)
    {
        Console.WriteLine(result);
    }
}

要进行超时,您必须在匿名方法中执行某些操作或使用CancellationToken

或者您可以使用 Task 对象本身:

public void DoStuff()
{
    int input = 2;

    Task<string> task = Task<string>.Factory.StartNew(() =>
        {
            var output = (input + 10).ToString();
            return output;
        });

    //use this if you want to wait for all your tasks to complete
    Task.WaitAll(task);

    //Calling result will implicitly cause a wait if the task is not complete
    Console.WriteLine(task.Result);
}

要使任务超时,您可以单独调用 Wait 并为所有任务设置超时,或者在 WaitAll 上指定超时,或者您可以再次使用 CancellationToken。

Task.WaitAll(new[] {task}, TimeSpan.FromSeconds(10));

编辑

我试图为您的示例提供一个实现,尽管有一些地方需要澄清,并且示例的某些部分没有关联,所以我不得不做出一些假设:

  1. 拨号 = 拨号详情
  2. lstDial = 拨号
  3. “如果我在 30 秒内没有收到任何对象的属性,我必须将该对象的属性设置为 NULL”表示您仍然想要其余的项目,而不是那个。

我已经使用并行 foreach 和任务的混合来完成它。

var dials = new List<DialDetails>();
Parallel.ForEach(dialsToShow, c =>
    {
        var task = Task<IList<string>>.Factory.StartNew(
            () => MyMethodThatCalls3rdPartyService(userName));
        // Get the item from the database while calling the service
        // However there appears to be no search going on here
        // the same type will always come back right?
        var dashboardObj = _repository.FindQueryable<DashboardDial_Tbl>()
                    .Select(p => new DialDetails()
                    {
                        Id = p.DialId,
                        Name= p.DialName,
                        Type = p.DialType      
                    }).SingleOrDefault();

        //Wait until the timeout
        task.Wait(TimeSpan.FromSeconds(30));

        //If it did not timeout or no other error occurred us the results
        if (dashboardObj != null && task.Status == TaskStatus.RanToCompletion)
        {
            //Need to do some checking here for indexes
            dashboardObj.IndividualStat = task.Result[0];
            dashboardObj.GroupStat = task.Result[1];
            dials.Add(dashboardObj);
        }
    });
reponse.dialsList = dials;

private IList<string> MyMethodThatCalls3rdPartyService(string userName)
{
    //Call the service
}

【讨论】:

  • 感谢您的代码。不过还有一个问题。将“IndividualStat”和“GroupStat”属性分配给正确的仪表板obj 吗?属性(以及它必须分配到的对象) ) 同步吗?
  • 关于您的假设 #3:是的。如果第 3 方服务关闭/在 30 秒内没有响应,我将发送仅具有其他属性(Id、Name、Type)的“dial”对象,并将“IndividualStat”和“GroupStat”设置为 string.empty .
  • @user1550951 是的,任务的结果将被分配给正确的 DialDetails,尽管您从存储库获取 DashboardDial_Tbl 的示例代码似乎总是返回相同的对象,这似乎不正确。我建议您在一个简单的项目中使用代码,而不要像解决方案的其余部分那样复杂,以便您了解代码在做什么。
猜你喜欢
  • 2023-03-25
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多