使用Parallel.Foreach c#.net4.5.1进行多线程处理

时间:2015-07-02 16:44:34

标签: c# multithreading linq parallel-processing .net-4.5

我有一个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({ }) )所以我的用户界面没有锁定我

1 个答案:

答案 0 :(得分:1)

有一些原则适用:

  1. Prefer Task.Run over Task.Factory.StartNew。我在博客上描述了为什么StartNew是危险的; Run是一种更安全,更现代的选择。
  2. 不要将async lambda传递给Parallel.ForEach。它没有意义,也无法正常工作。
  3. Task.Delay本身不做任何事情。您必须await或使用同步版本(Thread.Sleep)。
  4. (事实上,在你的情况下,内部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);
      });
    });