2016-12-13 21 views
3
var finalList = new List<string>(); 
var list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ................. 999999}; 

var init = 0; 
var limitPerThread = 5; 

var countDownEvent = new CountdownEvent(list.Count); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    new Thread(delegate() 
        { 
         Foo(listToFilter); 
         countDownEvent.Signal(); 
        }).Start();  
    init += limitPerThread; 
} 

//wait all to finish 
countDownEvent.Wait(); 


private static void Foo(List<int> listToFilter) 
{ 
    var listDone = Boo(listToFilter); 
    lock (Object) 
    { 
     finalList.AddRange(listDone); 
    } 
} 

这不:为什么简单的多任务不适用于多线程呢?

var taskList = new List<Task>(); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    var task = Task.Factory.StartNew(() => Foo(listToFilter)); 
    taskList.add(task); 
    init += limitPerThread; 
} 

//wait all to finish 
Task.WaitAll(taskList.ToArray()); 

这个过程必须建立在年底至少700个线程。当我使用线程运行时,它可以工作并创建所有这些线程。但与Task它不..看起来像它不是起始倍数Tasks异步。

我真的想知道为什么....任何想法?

EDIT

PLINQ另一个版本(如所建议的)。

var taskList = new List<Task>(list.Count); 
Parallel.ForEach(taskList, t => 
        { 
         var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
         Foo(listToFilter); 
         init += limitPerThread; 
         t.Start(); 
        }); 
Task.WaitAll(taskList.ToArray()); 

EDIT2:

public static List<Communication> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = new Ping().Send(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.TimedOut) && listIps.Count() > i+1) 
         continue; 
        if (oPing.Status.Equals(IPStatus.TimedOut)) 
        { 
         communication.Result = "NOK"; 
         break; 
        } 
        communication.Result = oPing.Status.Equals(IPStatus.Success) ? "OK" : "NOK"; 
        break; 
       } 
       if (listIps.Count() > i+1) 
        continue; 
       communication.Result = "NOK"; 
       break; 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 
+0

你可以一个接一个地处理元素吗?也就是说,你可以在单个元素上调用'Boo()'而不是列表? –

+0

我无法逐个处理它们。这是一个多重处理ips地址的过程...我需要在不到2分钟的时间内ping所有的(+ - 3000)。我需要存储结果。 –

+2

等等......只是**“Boo()”做了些什么,实际上呢?它不会简单地产生一个“ping”过程,是吗? –

回答

6

Task s的多线程不。他们可以使用,但大多数情况下它们实际上是用于相反的 - 在单个线程上进行多路复用。

要使用多线程任务,我建议使用Parallel LINQ。它里面有很多已经优化,如您列出的智能分区,只因为有AR CPU内核产卵尽可能多线程等等


了解Taskasync,认为它是这样的 - 典型工作负载通常包含需要等待的IO。也许你读过一个文件,或者查询一个webservice,或者访问一个数据库,或者其他的东西。重点是 - 你的线程会等待一段很长的时间(至少在CPU周期内),直到你得到一个远程目的地的响应。

在Olden Days™中,这意味着您的线程被锁定(暂停),直到响应结束。如果你想在此期间做其他事情,你需要产生一个新的线程。这是可行的,但不是太高效。每个操作系统线程都带有相当大的开销(内存,内核资源)。并且最终可能会有多个线程主动刻录CPU,这意味着操作系统需要在它们之间切换,以便每个线程都获得一些CPU时间,并且这些“上下文切换”非常昂贵。

async改变了工作流程。现在,您可以在同一个线程上执行多个工作负载。虽然其中一项工作是来自遥远来源的结果,但另一项工作可以介入并使用该线程去做其他有用的事情。当第二个工作负载达到自己的await时,第一个工作负载可以唤醒并继续。

毕竟,产生更多的线程比CPU核心没有意义。你不会以这种方式完成更多的工作。恰恰相反 - 更多的时间将花在切换线程上,而更少的时间可用于有用的工作。

这就是最初设计的Task/async/await。然而Parallel LINQ也利用了它,并将其重用于多线程。在这种情况下,你可以这样看待它 - 其他线程是你的主线程是主线程正在等待的“远程目的地”。

+0

好的文字,兄弟...我也尝试过PLINQ,但没有成功..我要编辑实施内的问题。我很感激你能否看看。 –

+2

“任务不是多线程” - 不,但并非总是如此,但StartNew()确实意味着至少将它们推送到ThreadPool。 –

+0

确实如此,但是这是一种常见的误解,认为async ==多线程。而且,OP似乎也这么想。 –

5

任务在线程池中执行。这意味着少数线程将服务于大量的任务。你有多线程,但不是每个任务产生的线程。

您应该使用任务。你应该尽量使用与你的CPU相同的线程。通常,线程池正在为你做这件事。

0

你是如何衡量表现的?你认为700线程的执行速度会比线程4执行的700更快吗?不,他们不会。

好像它不是开始倍数任务异步

你是怎么想出了这个?正如其他人在评论和其他答案中提出的建议,您可能需要删除线程创建,因为在创建700线程后,您的系统性能会下降,因为线程会在处理器时间内互相争夺,而无需更快地完成任何工作。

所以,你需要添加async/await您的IO操作,进入Foo方法,用SendPingAsync版本。此外,你的方法可以simplyfied,对于listIps.Count() > i + 1条件很多检查都是无用的 - 你这样做在for条件块:

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      var ping = new Ping(); 
      communication.Result = "NOK"; 

      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = await ping.SendPingAsync(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.Success) 
        { 
         communication.Result = "OK"; 
         break; 
        } 
       } 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 

与您的代码另一个问题是,PLINQ版本不是线程安全的:

init += limitPerThread; 

这可能会在并行执行时失败。您可能会出台一些辅助方法,如在this answer

private async Task<List<PingReply>> PingAsync(List<Communication> theListOfIPs) 
{ 
    Ping pingSender = new Ping(); 
    var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 10000)); 
    var results = await Task.WhenAll(tasks); 

    return results.ToList(); 
} 

而且做这种检查(try/catch逻辑简单删除):

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 
     var check = await PingAsync(listIps); 
     communication.Result = check.Any(p => p.Status.Equals(IPStatus.Success)) ? "OK" : "NOK"; 
    } 
} 

而且你或许应该是用Task.Run代替Task.StartNew确定你没有阻止UI线程。