【问题标题】:Using lock statement with ThreadPool in C#在 C# 中使用带有 ThreadPool 的 lock 语句
【发布时间】:2013-08-20 21:11:37
【问题描述】:

我有一个多线程程序 (C#),我必须在线程之间共享全局静态变量,这可能需要一些时间来执行(使用 WCF 向另一个系统发送数据请求)。问题是在线程池之外声明时,使用 lock 语句似乎并不能保证互斥。

static void Main(string[] args)
{
    public static int globalVar = 0;
    public object locker;

    System.Timers.Timer timer1 = new System.Timers.Timer(1000);
    timer1.Elapsed += new ElapsedEventHandler(onTimer1ElapsedEvent);
    timer1.Interval = 1000;
    timer1.Enabled = true;

    System.Timers.Timer timer2 = new System.Timers.Timer(500);
    timer2.Elapsed += new ElapsedEventHandler(onTimer2ElapsedEvent);
    timer2.Interval = 500;
    timer2.Enabled = true;
}

public void onTimer1ElapsedEvent(object source, ElapsedEventArgs e)
{
    lock (locker) {
        ThreadPool.QueueUserWorkItem(new WaitCallback(state => 
        {
            globalVar = 1;
            Console.WriteLine("Timer1 var = {0}", globalVar);
        }));
    } 
}
public void onTimer2ElapsedEvent(object source, ElapsedEventArgs e)
{
    lock (locker) {
        ThreadPool.QueueUserWorkItem(new WaitCallback(state => 
        {
            globalVar = 2;
            Thread.Sleep(2000);  // simulates a WCF request that may take time
            Console.WriteLine("Timer2 var = {0}", globalVar);
        }));
    } 
}

所以锁不起作用,程序可以打印:Timer2 var = 1

将lock语句放在ThreadPool中似乎可以解决问题。

public void onTimer1ElapsedEvent(object source, ElapsedEventArgs e)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(state =>
    {
        lock (locker) {           
            globalVar = 1;
            Console.WriteLine("Timer1 var = {0}", globalVar);
        } 
    }));    
}
public void onTimer2ElapsedEvent(object source, ElapsedEventArgs e)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(state =>
    {
        lock (locker) {           
            globalVar = 2;
            Thread.Sleep(2000);  // simulates a WCF request that may take time
            Console.WriteLine("Timer2 var = {0}", globalVar);
        } 
    }));    
}

但是,我不理解这两种方法之间的区别以及为什么它不会产生相同的行为。

另外,第二种方法解决了互斥问题,但 timer1 线程将始终必须等待 timer2 完成其锁定语句(这需要时间),因此多线程概念在我的程序中不再适用。我想知道让多线程与使用共享变量并行工作的最佳解决方案是什么?

【问题讨论】:

    标签: c# multithreading locking threadpool


    【解决方案1】:

    你不需要锁来更新这样的变量。例如,您可以替换为:

    lock (locker)
    {
        globalVar = 1;
        Console.WriteLine("Timer1 var = {0}", globalVar);
    }
    

    与:

    int val = 1;
    globalVar = val;
    Console.WriteLine("Timer1 var = {0}", val);
    

    对原始类型的写入保证是原子的,因此无需在此处锁定。

    现在,如果你想增加一个值,你可以这样写:

    int val = Interlocked.Increment(ref globalVar);
    

    您还可以添加:

    int val = Interlocked.Add(ref globalVar, 100);
    

    同样,这些不需要锁。

    查看Interlocked 类。

    【讨论】:

      【解决方案2】:

      在您的第一个场景中,您所锁定的只是在 ThreadPool 上添加一个新的 WaitCallback。将 ThreadPool 视为一条线:您所做的只是锁定让其他人排队(具有讽刺意味的是,这实际上是双重工作,因为 ThreadPool 本身锁定在它维护的内部队列上)。 ThreadPool 之后执行的代码在不同的线程上,发生在不同的时间,与那个锁无关了。

      在您的第二种情况下,锁实际上是线程池线程正在执行的代码中,这就是您看到预期的锁定语义的原因。

      不过,一般来说,如果可以避免的话,我建议不要锁定 ThreadPool 线程。应该(理想情况下)将 ThreadPool 用于快速运行的任务。这取决于共享状态的性质和用途,以及您要完成的工作,但总的来说,我会尽可能选择使用Tasks 和/或PLINQ

      【讨论】:

        【解决方案3】:

        更短更明智的解决方案是不使用(又一个)额外线程来执行 Timer。 System.Timers.Timer 已经分配了一个池线程。

        public void onTimer1ElapsedEvent(object source, ElapsedEventArgs e)
        {
            lock (locker) {
                    globalVar = 1;
                    Console.WriteLine("Timer1 var = {0}", globalVar);
        
            } 
        }
        public void onTimer2ElapsedEvent(object source, ElapsedEventArgs e)
        {
            lock (locker) {
                    globalVar = 2;
                    Thread.Sleep(2000);  // simulates a WCF request that may take time
                    Console.WriteLine("Timer2 var = {0}", globalVar);
            } 
        }
        

        您的困惑来自诸如“将锁定语句放入线程池”之类的公式。

        您将锁定语句放在方法中以控制它们运行的​​线程。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-26
          • 1970-01-01
          • 2022-06-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多