并行多线程处理.Foreach c#.net4.5.1

本文关键字:net4 多线程处理 Foreach 并行 | 更新日期: 2023-09-27 18:00:54

我有一个IEnumerable<customClass>对象,它大约有10-15个条目,所以不是很多,但当我尝试进行时,会遇到System.IO.FileNotFoundException

Parallel.Foreach(..some linq query.., object => { ...stuff....});

带有可枚举项。以下是我的代码,有时有效,有时无效:

        IEnumerable<UserIdentifier> userIds = script.Entries.Select(x => x.UserIdentifier).Distinct();
        await Task.Factory.StartNew(() =>
        {
            Parallel.ForEach(userIds, async userId =>
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                _Log.InfoFormat("user identifier: {0}", userId);
                await Task.Factory.StartNew(() =>
                {
                    foreach (ScriptEntry se in script.Entries.Where(x => x.UserIdentifier.Equals(userId)))
                    {
                        //    // Run the script //
                        _Log.InfoFormat("waiting {0}", se.Delay);
                        Task.Delay(se.Delay);
                        _Log.InfoFormat("running SelectionInformation{0}", se.SelectionInformation);
                        ExecuteSingleEntry(se);
                        _Log.InfoFormat("[====== SelectionInformation {1} ELAPSED TIME: {0} ======]", watch.Elapsed,
                            se.SelectionInformation.Verb);
                    }
                });
                watch.Stop();
                _Log.InfoFormat("[====== TOTAL ELAPSED TIME: {0} ======]", watch.Elapsed);
            });
        });

当函数ExecuteSingleEntry运行时,在该函数的深处有一个函数,它可以创建临时目录和文件。在我看来,当我运行parallel.foreach时,该函数一次被多次调用猛烈抨击(我目前正在一次测试5,但需要处理大约10个(,并且没有创建我需要的一些文件。但是,如果我在文件创建函数中遇到了一个断点,并且每次只命中F5,那么抛出未找到文件的异常就不会有任何问题。

因此,我的问题是,如何实现基于脚本条目中的用户id并行运行scripts.Entries的子集,并且每个不同的用户id条目之间的延迟为1秒

脚本条目类似于:

UserIdentifier: 141, SelectionInformation: class of stuff, Ids: list of EntryIds, Names: list of Entry Names

并且每个用户标识符可以在数组中出现1次或多次。我想同时启动所有不同的用户标识符,或多或少。然后Task输出绑定到脚本条目的不同SelectionInformation。

scripts.EntriesScriptEntry的数组,如下所示:

    [DataMember]
    public TimeSpan Delay { get; set; }
    [DataMember]
    public SelectionInformation Selection { get; set; }
    [DataMember]
    public long[] Ids { get; set; }
    [DataMember]
    public string Names { get; set; }
    [DataMember]
    public long UserIdentifier { get; set; }

我引用了:Parallel.ForEach vs Task.Factory.StartNew以获得

Task.Factory.StartNew(() => Parallel.Foreach({ }) ),这样我的UI就不会锁定我的

并行多线程处理.Foreach c#.net4.5.1

有几个原则可以应用:

  1. Task.Factory.StartNew相比,更喜欢Task.Run。我在博客上描述了为什么StartNew是危险的;Run是一种更安全、更现代化的替代品
  2. 不要将async lambda传递给Parallel.ForEach。这没有道理,也不会正常工作
  3. Task.Delay本身不做任何事情。您必须将其await或使用同步版本(Thread.Sleep(

(事实上,在您的情况下,内部StartNew是没有意义的;它已经是并行的,并且在线程池线程上运行的代码正试图在线程池上启动一个新操作,并立即异步等待它??(

应用这些原则后:

await Task.Run(() =>
{
  Parallel.ForEach(userIds, userId =>
  {
    Stopwatch watch = new Stopwatch();
    watch.Start();
    _Log.InfoFormat("user identifier: {0}", userId);
    foreach (ScriptEntry se in script.Entries.Where(x => x.UserIdentifier.Equals(userId)))
    {
      //    // Run the script //
      _Log.InfoFormat("waiting {0}", se.Delay);
      Thread.Sleep(se.Delay);
      _Log.InfoFormat("running SelectionInformation{0}", se.SelectionInformation);
      ExecuteSingleEntry(se);
      _Log.InfoFormat("[====== SelectionInformation {1} ELAPSED TIME: {0} ======]", watch.Elapsed,
          se.SelectionInformation.Verb);
    }
    watch.Stop();
    _Log.InfoFormat("[====== TOTAL ELAPSED TIME: {0} ======]", watch.Elapsed);
  });
});