处理并行作业/线程

时间:2018-08-05 21:48:55

标签: c# multithreading task spinlock interlocked-increment

我正在尝试重构我的项目,现在我正在尝试寻找最佳方法以提高应用程序的性能。

问题1.自旋锁与互锁

要创建一个计数器,该方法具有更好的性能。

Interlocked.increament(ref counter)

SpinLock _spinlock = new SpinLock()
bool lockTaken = false;
try
{
    _spinlock.Enter(ref lockTaken);
    counter = counter + 1;                
}
finally
 { 
     if (lockTaken) _spinlock.Exit(false);
 } 

如果我们需要给另一个计数器(例如counter2)加油,是否应该声明另一个SpinLock对象?还是足以使用另一个boolean对象?

问题2。处理嵌套任务或更好的替换

在此应用程序的当前版本中,我使用任务,将每个新任务添加到数组中,然后使用Task.WaitAll()

经过大量研究,我发现使用Parallel.ForEach具有更好的性能,但是如何控制当前线程的数量呢?我知道我可以在MaxDegreeOfParallelism参数中指定ParallelOptions,但是问题出在这里,每次运行crawl(url)方法时,它只会创建另一个有限数量的线程,这意味着如果我设置了{ {1}}到10,每次运行MaxDegree时,都会创建另一个+10,对吗?那么如何防止这种情况发生?我应该使用信号量和线程代替Paralel吗?还是有更好的方法?

crawl(url)

问题3.所有作业(和嵌套作业)完成时通知。

我的最后一个问题是,我所有工作完成后如何理解?

致谢。

2 个答案:

答案 0 :(得分:1)

这里有很多误解,请指出几个。

  

要创建一个计数器,哪种方法具有更好的性能。

两者都取决于您的确切情况

  

经过大量研究,我只是使用Parallel.ForEach进行了研究   性能更好

这也是非常可疑的,实际上是错误的。再次取决于您要做什么。

  

我知道我可以在ParallelOptions中指定MaxDegreeOfParallelism   参数,但是问题出在这里,每次执行crawl(url)方法时,它只会创建另一个有限数量的线程

再一次,这是错误的,这是您自己的实现细节,并取决于您如何执行。而且 TPL MaxDegreeOfParallelism只是一个建议,它只会做它认为对您最有利的尝试。

  

我应该使用信号量和线程代替Paralel吗?或有一个   更好的方法?

答案是肯定的。


好,让我们看看您在做什么。你说你正在做一个履带。每次您访问Internet或网络资源或文件系统时,爬虫都会访问Internet,(简而言之)您正在等待IO完成端口回调。这就是所谓的 IO工作负载

使用 IO Bound 任务,我们不想将线程池与等待 IO完成端口的线程捆绑在一起。它效率低下,您正在消耗宝贵的资源,以等待有效暂停的线程上的回调。

因此,对于 IO绑定工作,我们不想启动新任务,也不想使用 Parallel ForEach 等待使用完线程等待事件发生。用于 IO绑定任务的最合适的现代模式是asyncawait模式。

对于CPU限制的工作(如果要使用尽可能多的CPU),请粉碎线程池,请使用 TPL Parallel 或许多有效的任务。

asyncawait模式在完成端口上可以很好地工作,因为与其闲置地等待回调,不如将其退回并允许它们被重用。是的

...

但是我建议使用的是另一种方法,您可以利用asyncawait并控制并行度。这使您可以更好地使用线程池,而不会用尽资源等待回调,并允许IO成为IO。我给你TPL DataFlow ActionBlockTransformManyBlocks


这个主题比一个简单的工作示例略高一点,但是我可以向您保证,它是您正在做的事情的合适路径。我建议您看看以下链接。

总而言之,有很多方法可以做您想做的事,并且有很多技术。但是最主要的是,您对并行编程有一些非常复杂的想法。您需要阅读书籍,浏览博客,并开始从头开始获得一些真正扎实的设计原则,并停止尝试通过收集少量信息来为自己解决所有问题。

祝你好运

答案 1 :(得分:0)

我建议为此考虑一下Microsoft的Reactive Framework。您可以这样编写Crawl函数:

public IObservable<Response> Crawl(string url)
{
    return
        from r in Observable.Start(() => GetResponse(url))
        from l in r.Links.ToObservable()
        from r2 in Crawl(l).StartWith(r)
        select r2;
}

然后称呼它试试

IObservable<Response> crawls = Crawl("www.microsoft.com");

IDisposable subscription =
    crawls
        .Subscribe(
            r => { /* process each response as it arrives */ },
            () => { /* All crawls complete */ });

完成。它为您处理所有线程。只是NuGet“ System.Reactive”。