任务背景工人c#

时间:2012-05-24 19:33:04

标签: c# multithreading backgroundworker task

在5个第二个正在运行的进程中,多个后台工作程序的执行效果是否优于任务?我记得在一本书中读到一个任务是为短时间运行的过程而设计的。

我问的问题是:

我有一个需要5秒才能完成的过程,并且有4000个过程需要完成。起初我做了:

for (int i=0; i<4000; i++) {
    Task.Factory.StartNewTask(action);
}

并且性能很差(在第一分钟之后,3-4个任务完成,控制台应用程序有35个线程)。也许这是愚蠢的,但我认为线程池将处理这种情况(它会将所有操作放在一个队列中,当一个线程空闲时,它将采取行动并执行它。)

现在的第二步是手动执行Environment.ProcessorCount后台工作程序,并将所有操作放在ConcurentQueue中。所以代码看起来像这样:

var workers = new List<BackgroundWorker>();
//initialize workers

workers.ForEach((bk) =>
{
    bk.DoWork += (s, e) =>
    {
        while (toDoActions.Count > 0)
        {
            Action a;
            if (toDoActions.TryDequeue(out a))
            {
                a();
            }
        } 
    }

    bk.RunWorkerAsync();
});

这样做的方式更好。即使我有30名背景工作人员(与第一种情况一样多的任务),它的表现要比任务好得多。

LE

我像这样启动任务:

    public static Task IndexFile(string file)
    {
        Action<object> indexAction = new Action<object>((f) =>
        {
            Index((string)f);
        });

        return Task.Factory.StartNew(indexAction, file);
    }

Index方法就是这个:

    private static void Index(string file)
    {
        AudioDetectionServiceReference.AudioDetectionServiceClient client = new AudioDetectionServiceReference.AudioDetectionServiceClient();

        client.IndexCompleted += (s, e) =>
            {
                if (e.Error != null)
                {
                    if (FileError != null)
                    {
                        FileError(client, 
                            new FileIndexErrorEventArgs((string)e.UserState, e.Error));
                    }
                }
                else
                {
                    if (FileIndexed != null)
                    {
                        FileIndexed(client, new FileIndexedEventArgs((string)e.UserState));
                    }
                }
            };

        using (IAudio proxy = new BassProxy())
        {
            List<int> max = new List<int>();
            if (proxy.ReadFFTData(file, out max))
            {
                while (max.Count > 0 && max.First() == 0)
                {
                    max.RemoveAt(0);
                }
                while (max.Count > 0 && max.Last() == 0)
                {
                    max.RemoveAt(max.Count - 1);
                }

                client.IndexAsync(max.ToArray(), file, file);
            }
            else
            {
                throw new CouldNotIndexException(file, "The audio proxy did not return any data for this file.");
            }
        }
    }

此方法使用Bass.net库从mp3文件中读取一些数据。然后使用异步方法将该数据发送到WCF服务。 创建任务的IndexFile(字符串文件)方法在for循环中调用4000次。 这两个事件,FileIndexed和FileError都没有被处理,因此它们永远不会被抛出。

3 个答案:

答案 0 :(得分:1)

鉴于您有严格定义的事项列表,我会使用Parallel类(ForForEach,具体取决于您的选择)。此外,您可以将配置参数传递给任何这些方法,以控制实际执行的任务数量:

        System.Threading.Tasks.Parallel.For(0, 20000, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, i =>
        {
            //do something
        });

上述代码将执行20000次操作,但不会同时执行5次以上的操作。

我怀疑后台工作人员为你做得更好的原因是因为你在开始时创建并实例化了它们,而在你的示例Task代码中,你似乎正在创建一个新的Task对象对于每一项行动。

或者,您是否考虑过使用一开始实例化的固定数量的Task个对象,然后像对待后台工作者一样使用ConcurrentQueue执行类似的操作?这也应该证明是非常有效的。

答案 1 :(得分:1)

任务的性能如此差的原因是因为你安装了太多的小任务(4000)。请记住,CPU也需要安排任务,因此安装大量短期任务会给CPU带来额外的工作量。更多信息可以在TPL的第二段中找到:

  

从.NET Framework 4开始,TPL是首选方式   编写多线程和并行代码。但是,并非所有代码都是   适合并行化;例如,如果一个循环只执行一个   每次迭代的工作量很少,或者很多都没有   迭代,然后并行化的开销可以导致代码   运行得更慢。

当您使用后台工作程序时,您将可能的活动线程数限制为 ProcessCount 。这减少了大量的调度开销。

答案 2 :(得分:0)

您是否考虑过使用线程池?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

如果在使用线程时性能较慢,则只能由于线程开销(分配和销毁单个线程)而造成的。