信号量阻塞主进程而不是多线程

时间:2013-07-12 23:05:20

标签: c# multithreading

所以根据MSDN以及我读过的许多其他地方,他们在各个线程中使用信号量并阻塞,如下所示:

private static Semaphore _pool;
public static void Main()
{
    _pool = new Semaphore(0, 3);
    for(int i = 1; i <= 1000; i++)
    {
        Thread t = new Thread(new ParameterizedThreadStart(Worker));
        t.Start(i);
    }
}

private static void Worker(object num)
{
    try
    {
        _pool.WaitOne();
        // do a long process here
    }
    finally
    {
        _pool.Release();
    }
}

根据Main()中的迭代次数,阻止进程是不是更有意义,以便不会同时创建可能的1000个线程?例如:

private static Semaphore _pool;
public static void Main()
{
    _pool = new Semaphore(0, 3);
    for(int i = 1; i <= 1000; i++)
    {
        _pool.WaitOne(); // wait for semaphore release here

        Thread t = new Thread(new ParameterizedThreadStart(Worker));
        t.Start(i);
    }
}

private static void Worker(object num)
{
    try
    {
        // do a long process here
    }
    finally
    {
        _pool.Release();
    }
} 

也许两种方式都没有错,这取决于具体情况?或者,一旦有很多迭代,有更好的方法可以做到这一点吗?

编辑:这是一个Windows服务,所以我没有阻止UI线程。

1 个答案:

答案 0 :(得分:2)

您通常在线程中执行此操作的原因是您希望使该独占部分尽可能小。您不需要整个线程同步,只需要该线程访问共享资源。

所以更真实的工作版本是

private static void Worker(object num)
{
    //Do a bunch of work that can happen in parallel
    try
    {
        _pool.WaitOne();
        // do a small amount of work that can only happen in 3 threads at once
    }
    finally
    {
        _pool.Release();
    }
    //Do a bunch more work that can happen in parallel
}

(PS如果您正在做一些使用1000个线程的事情,那么您做错了什么。您可能宁愿使用ThreadPoolTasks来处理许多短期工作负载或让每个线程都做更多的工作。)


以下是使用Parallel.ForEach

的方法
private static BlockingCollection<int> _pool;
public static void Main()
{
    _pool = new BlockingCollection<int>();
    Task.Run(() => //This is run in another thread so it shows data is being taken out and put in at the same time
    {
        for(int i = 1; i <= 1000; i++)
        {
            _pool.Add(i);
        }
        _pool.CompleteAdding(); //Lets the foreach know no new items will be showing up.
    });

    //This will work on the items in _pool, if there is no items in the collection it will block till CompleteAdding() is called.
    Parallel.ForEach(_pool.GetConsumingEnumerable(), new ParallelOptions {MaxDegreeOfParallelism = 3}, Worker);
}

private static void Worker(int num)
{
        // do a long process here
}