【问题标题】:.NET Threadpool synchronizing using AutoResetEvent.NET 线程池使用 AutoResetEvent 进行同步
【发布时间】:2013-07-12 19:37:58
【问题描述】:

以下是我使用的代码。主线程等待线程池线程执行。我使用 AutoResetEvent (WaitHandle),但我真的很惊讶我偏离了标准,因为代码没有按预期运行。

我有两个同心的for循环,其中Threadpool在内部循环中,并且预计对于外部循环的每次迭代,都应该处理所有内部循环值。使用 AutoResetEvent WaitOne 在内部循环外部调用使主线程等待,这是一个静态变量,在外部循环的每次迭代中重置为内部循环的最大值,并在使用 Threadpool 线程的方法调用中使用 Interlock 递减为 AutoResetEvent 调用 Set。但是,即使我希望静态变量在每个内部循环之后显示值 0,它也不会。我的代码有什么问题,我有什么更好的选择来完成任务?事实上,由于值的混淆,主线程似乎并没有真正在等待线程池线程。

using System;
using System.Threading;

namespace TestThreads
{
class Program
{
    private static int threadingCounter = 0;
    private static readonly object lockThreads = new Object();
    private AutoResetEvent areSync = new AutoResetEvent(true);

    // <param name="args"></param>
    static void Main(string[] args)
    {
        Program myProgram = new Program();

        try
        {
            try
            {   
                for (int outer = 0; outer < 1000; outer++)
                {
                    threadingCounter = 500;
                    try
                    {
                        for (int inner = 0; inner < 500; inner++)
                        {
                            ThreadPool.QueueUserWorkItem(new
                                 WaitCallback(myProgram.ThreadCall), inner);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Exception :: " + ex.Message);
                    }
                    finally
                    {                            
                        myProgram.areSync.WaitOne();
                    }

                    if(threadingCounter != 0)
                        Console.WriteLine("In Loop1, Thread Counter :: " +
                            threadingCounter);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception :: " + ex.Message);
            }                
        }
        catch(Exception ex)
        {
            Console.WriteLine("Exception :: " + ex.Message);
        }
        finally
        {
            threadingCounter = 0;

            if (myProgram.areSync != null)
            {
                myProgram.areSync.Dispose();
                myProgram.areSync = null;
            }
        }
    }

    public void ThreadCall(object state)
    {
        try
        {
            int inner = (int)state;
            Thread.Sleep(1);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception :: " + ex.Message);
        }
        finally
        {
            Interlocked.Decrement(ref threadingCounter);
            if (threadingCounter <= 0)
                areSync.Set();
        }                
    }        
 }
}

【问题讨论】:

  • 你的异常处理太糟糕了。至少有一个错误可见,您没有正确使用 Interlocked.Decrement。您必须使用它的返回值并且从不在变量递减时直接使用它。使用 CountDownEvent 跌入成功的坑。
  • @HansPassant,你的评论很粗鲁,实际上不准确。
  • 好吧,我被欺负而改写了。您的异常处理大大提高了代码的可读性。访问被其他线程修改的变量不是问题。更好?
  • @HansPassant,谢谢你的评论,当你说对了的时候不必被欺负,我知道异常处理很糟糕,但请注意我粘贴了我添加的代码尝试最终从各个方面赶上,重点是了解线程同步的问题并确保进行正确的调用,尽管我从未添加过干净的专业代码。但是,当您提出这样的建议时,请至少提供一个您认为很好的异常处理示例,因为这将有助于更好地理解您的观点,谢谢,
  • @Tyler Jensen 感谢您的评论,但到目前为止,HansPassant 的建议有效,我能够更正代码。但是,如果在多线程方案中对异常处理有任何好的做法,请提出建议。

标签: c# .net multithreading threadpool


【解决方案1】:

您已初始化 AutoResetEvent,初始状态为 signaled(true),这将允许首次调用

myProgram.areSync.WaitOne();

继续不阻塞,所以它继续外循环并再次将队列执行到线程池,因此结果混乱。很清楚。

更新你的代码

private AutoResetEvent areSync = new AutoResetEvent(false);

对于预期的结果。希望这会有所帮助

【讨论】:

  • AutoResetEvent 初始化值为 true 是我的帖子中的一个错误,因为我尝试了多种方法,所以我将其设为 false,这没有帮助。任何其他可能有帮助的观点
  • 不,设置新的 AutoResetEvent(false) 对我有用,我测试了你的代码。
  • 上述汉斯的建议更正了代码,否则不正确。会导致问题离子多线程场景
【解决方案2】:

我会用这样的东西来重构它。这假设您想在内部循环的后台线程中执行某些操作,在继续执行下一个外部循环之前完成每个线程,避免混乱的异常处理,同时仍捕获处理期间发生的异常,以便您可以在处理后处理这些异常内循环和外循环都是完整的。

// track exceptions that occurred in loops
class ErrorInfo
{
    public Exception Error { get; set; }
    public int Outer { get; set; }
    public int Inner { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // something to store execeptions from inner thread loop
        var errors = new ConcurrentBag<ErrorInfo>();
        // no need to wrap a try around this simple loop
        // unless you want an exception to stop the loop
        for (int outer = 0; outer < 10; outer++)
        {
            var tasks = new Task[50];
            for (int inner = 0; inner < 50; inner++)
            {
                var outerLocal = outer;
                var innerLocal = inner;
                tasks[inner] = Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            Thread.Sleep(innerLocal);
                            if (innerLocal % 5 == 0)
                            {
                                throw new Exception("Test of " + innerLocal);
                            }
                        }
                        catch (Exception e)
                        {
                            errors.Add(new ErrorInfo
                            {
                                Error = e,
                                Inner = innerLocal,
                                Outer = outerLocal
                            });
                        }
                    });
            }
            Task.WaitAll(tasks);
        }
        Console.WriteLine("Error bag contains {0} errors.", errors.Count);
        Console.ReadLine();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 2018-03-10
    • 2011-11-03
    • 2023-03-12
    • 2011-04-15
    相关资源
    最近更新 更多