2010-08-24 11 views
2

有没有更好的方法来实现像下面这样的简单锁定?我不知道是否有更好的方法来实现这个“简单的锁”

我只想到“DOSOMETHING”,如果它尚未运行。我应该在这里使用reall锁吗?如果我使用lock会导致一切排队并等待锁释放? (这不是我想要的!)

感谢

bool running = false; 

    void DataDisplayView_Paint(object sender, PaintEventArgs e) 
    { 
    // if (!this.initialSetDone) 
    if (!running) 
    { 
     this.running = true; 

     //DOSOMETHING 

     this.running = false; 
    } 
} 
+0

我读过你的问题多次,其根本不清楚你想要做什么。你能提供一些背景知识或更完整的描述你的代码应该做什么吗? – Juliet 2010-08-24 15:43:30

+1

到目前为止,所有答案一般都存在于两个阵营之一1)您使用多个线程并且您描述的是线程同步问题或2)您没有使用多个线程,而且您正在描述方法重入问题。你可以通过更新来澄清你的问题吗?根据我迄今看到的情况,我假定了后者。那是对的吗? – 2010-08-24 16:08:03

+0

@朱丽叶,对不起使用“initialSetDone”而不是“运行”混淆了这个问题。 @布莱恩,是的,这是一个重入问题没问题。答案似乎表明我使用的方法是正确的,但可以通过使用try catch来改进 – AidanO 2010-08-25 07:05:18

回答

5

不,你不想在这里使用锁。这不是线程同步问题。这是一个方法重入问题。

你可能会尝试这样的事情。

bool running = false; 

void DataDisplayView_Paint(object sender, PaintEventArgs e) 
{ 
    if (!this.running) 
    { 
    this.running = true; 
    try 
    { 
     //DOSOMETHING 
    } 
    finally 
    { 
     this.running = false; 
    } 
    } 
} 
+1

问题是,在第一个管理器设置running = true之前,两个线程可以通过检查(!!运行)。在这种情况下,最终会有2个线程同时运行DOSOMETHING。 – Grzenio 2010-08-24 15:45:03

+2

@Grzenio:没错,但是这个问题从来没有暗示过涉及到多线程以及所涉及方法的签名似乎证实了这一点。这就是在单个线程上执行的'Control.Paint'事件... UI线程。鉴于我认为假设OP描述重入问题而不是线程同步问题更安全。 – 2010-08-24 16:03:31

+0

@布莱恩:公平点 – Grzenio 2010-08-24 19:10:00

1

最好的方法是使用try/finally块

try { 
    this.running = true; 
    ... 
} finally { 
    this.running = false; 
} 

真实螺纹锁只需要如果此方法是从所谓的多线程。鉴于它看起来是一个绘制事件处理程序,这不太可能,因为控件是关联到单个线程的。

2

你只需要同步(锁是最简单的方法)的代码位:

bool running = false; 
readonly object padlock = new object(); 

    void DataDisplayView_Paint(object sender, PaintEventArgs e) 
    { 

    if (!this.initialSetDone) 
    { 
     lock(padlock) 
     { 
      if(running) return; 
      running = true; 
     } 
     try { 

      //DOSOMETHING 
     } 
     finally 
     { 
      lock(padlock) 
      { 
      this.running = false; 
      } 
     } 
    } 
} 
+0

DOSOMETHING方法倾向于扔;) – 2010-08-24 15:21:48

+1

我总是让我的锁对象'只读'。 – row1 2010-08-24 15:30:04

+0

添加了try/finally和readonly,干杯。 – Grzenio 2010-08-24 15:32:32

0

如果使用Monitor.TryEnter相反,你可以指定超时,在这种情况下,结果你得到的是这样的:

  • 只有一个线程可以同时运行DoSomething的
  • 后续调用将尝试获得锁和AFTE放弃R上的超时条款

如果不以超时提供,或超时设置为0,这个调用不会阻止,并立即返回(也许这会满足您的要求更好?):

if (!this.initialSetDone && Monitor.TryEnter(_lock)) 
{ 
    // DOSOMETHING 
} 

另外,也可以使running变量挥发,使你总是会得到存储在变量的最新值:

private volatile bool running; 

if (!this.initialSetDone && !this.running) // #1 
{ 
    this.running = true; 
    try 
    { 
    // DOSOMETHING 
    } 
    finally 
    { 
    this.running = false; 
    } 
} 

第二种方法不会排队后续调用,但有该两个线程都会碰到#1并评估它可以安全地继续运行的可能性,然后两者都会运行DOSOMETHING,尽管这种可能性不大。

0

您在中班varaible的名字,所以我会假设你想:

bool running = false; 

    void DataDisplayView_Paint(object sender, PaintEventArgs e) 
    { 
    if (!this.running) 
    { 
     this.running = true; 

     //DOSOMETHING 

     this.running = false; 
    } 
} 

你有这里的问题是,如果DataDisplayView_Paint可以从多个线程调用,那么它有可能在if (!this.running)this.running = true;之间,另一个线程可以跳转并启动DOSOMETHING(因为运行仍然是错误的)。然后第一个线程将恢复,并再次启动DOSOMETHING。如果这是可能的话,那么你将需要使用真正的锁。

1

我错过了什么吗?你发布的代码似乎没有做任何事情。也就是说,无论running是否为真,代码都会运行。

一般情况下,任何试图“锁定”本身任何像这样的代码......

if (!running) 
{ 
    running = true; 

    try 
    { 
     // This code should not call itself recursively. 
     // However, it may execute simultaneously on more than one thread 
     // in very rare cases. 
    } 
    finally 
    { 
     running = false; 
    } 
} 

...是非常好的,只要你在单线程的情况是。如果您正在运行多线程代码,则可能会出现问题,因为您认为没有两个线程会同时到达if (!running)行。

多线程代码中的解决方案是使用某种形式的原子开关。我已经使用了AutoResetEvent此目的:

var ready = new AutoResetEvent(true); 

if (ready.WaitOne(0)) 
{ 
    try 
    { 
     // This code will never be running on more than one thread 
     // at a time. 
    } 
    finally 
    { 
     ready.Set(); 
    } 
} 
+0

谢谢丹,很好看,我会编辑代码 – AidanO 2010-08-25 06:59:04

1

需要注意的是,如果你有你的油漆回调重入,你有一个更严重的问题。油漆处理程序应该阻止你的消息泵(并且应该相对快速地完成),所以你不应该看到这种情况。唯一的例外是如果你从你的paint handler的某个地方调用Application.DoEvents(),你真的不应该这样做。

+0

好点丹,也许油漆是一个不好的例子。 – AidanO 2010-08-25 06:59:59

0

我只是想给“的DoSomething”如果 它尚未被运行

你的问题没有足够的信息,让我不禁做出你的代码的假设。

  • 我的第一个假设是,根据签名DataDisplayView_Paint(object s, PaintEventArgs e),您的代码在GUI线程上运行。

  • 我的第二个假设是您的代码DOSOMETHING是同步的。

考虑到这一点,这里是你的代码的版本,这保证了我们只运行DOSOMETHING如果尚未被运行:

void DataDisplayView_Paint(object s, PaintEventArgs e) 
{ 
    //DOSOMETHING 
} 

GUI线程会一次只处理一个消息,您的DataDisplayView_Paint方法不会退出,直到DOSOMETHING完成。如果您使用GUI执行任何操作(如绘制Graphics对象或更改标签),则不会从多个线程调用此代码 - 如果这样做,.NET将抛出异常。换句话说,你不需要需要任何同步。


假设DOSOMETHING运行asyncronously - 现在我们有一个有趣的问题,但它很容易解决的,你不需要任何的bool。

实质上,您所做的只是在DOSOMETHING运行时禁用您的事件处理程序,然后重新启用它。而不是使用一个布尔的,脱钩,并根据需要rehook事件处理程序:

void DataDisplayView_Paint(object s, PaintEventArgs e) 
{ 
    DataDisplayView.Paint -= DataDisplayView_Paint; 
    DoSomethingAsynchronously(); // re-hooks event handler when completed 
} 

void DoSomethingAsychronously() 
{ 
    ThreadPool.QueueUserWorkItem(() => 
    { 
     try 
     { 
      // DOSOMETHING 
     } 
     finally 
     { 
      // may need a lock around this statement 
      DataDisplayView.Paint += DataDisplayView_Paint; 
     } 
    }); 
} 
相关问题