C# 我想知道有没有更好的方法来实现这一点。”;“简易锁”;

C# 我想知道有没有更好的方法来实现这一点。”;“简易锁”;,c#,locking,nonblocking,C#,Locking,Nonblocking,有没有更好的方法实现下面这样一个简单的锁 我只想在“DOSOMETHING”还没有运行的情况下使用它。我应该在这里使用reall锁吗?如果我使用锁,会导致所有东西排队等待锁释放吗?(这不是我想要的!) 谢谢 bool running = false; void DataDisplayView_Paint(object sender, PaintEventArgs e) { // if (!this.initialSetDone) if (!running)

有没有更好的方法实现下面这样一个简单的锁

我只想在“DOSOMETHING”还没有运行的情况下使用它。我应该在这里使用reall锁吗?如果我使用锁,会导致所有东西排队等待锁释放吗?(这不是我想要的!)

谢谢

  bool running = false;

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

        //DOSOMETHING

        this.running = false;
     }
 }

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

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

只有从多个线程调用此方法时,才需要真正的线程锁。鉴于它似乎是一个绘制事件处理程序,这不太可能,因为控件与单个线程密切相关。

否,您不希望在此处使用锁。这不是线程同步问题。这是一个方法可重入性问题

你可以试试这样的

bool running = false; 

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

您只需同步(锁定是最简单的方式)代码位:

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;
          }
        }
     }
 }

如果改用Monitor.TryEnter,则可以指定超时,在这种情况下,得到的结果如下:

  • 一次只能有一个线程运行DOSOMETHING
  • 后续调用将尝试获取锁并在超时子句之后放弃
如果您不提供超时,或将超时设置为0,则此调用不会被阻止并将立即返回(也许这更适合您的要求?)

或者,您可以将
running
变量设置为volatile,以便始终获得存储在变量中的最新值:

private volatile bool running;

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

第二种方法不会排队后续调用,但有可能两个线程都会命中1,并评估它是安全进行的,然后两个都结束运行,虽然这是极不可能的。

< P>你在中间移动可变名称,所以我假设你想要:

  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(因为running仍然是false)。然后,第一个线程将继续,并再次开始DOSOMETHING。如果这是可能的,那么您将需要使用真正的锁。

我遗漏了什么吗?您发布的代码似乎没有任何作用。也就是说,无论
running
是否为true,代码都将运行

通常,任何试图像这样“锁定”自身的代码

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();
    }
}

请注意,如果在绘制回调中有重入,则会出现更严重的问题。绘制处理程序应该阻塞消息泵(并且应该相对快速地完成),所以您永远不会看到这种情况。唯一的例外是,如果您从paint处理程序中的某个位置调用Application.DoEvents(),您确实不应该这样做

我只想去“DOSOMETHING”如果 它还没有运行

您的问题没有足够的信息,因此我不得不对您的代码进行假设

  • 我的第一个假设是,基于签名
    DataDisplayView\u Paint(objects s,PaintEventArgs e)
    ,您的代码在GUI线程上运行

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

记住这一点,下面是您的代码版本,它保证我们只运行
DOSOMETHING
,如果它还没有运行:

void DataDisplayView_Paint(object s, PaintEventArgs e)
{
    //DOSOMETHING
}
GUI线程一次只处理一条消息,
DataDisplayView\u Paint
方法在
DOSOMETHING
完成之前不会退出。如果您正在使用GUI执行任何操作,如绘制图形对象或更改标签,那么此代码将不会从多个线程调用——如果调用,则,.NET将抛出异常。换句话说,您不需要任何同步


让我们假设
DOSOMETHING
异步运行——现在我们有一个有趣的问题,但它很容易解决,并且不需要任何布尔运算

实际上,您所做的只是在DOSOMETHING运行时禁用事件处理程序,然后重新启用它。不使用bool,而是根据需要取消钩住并重新钩住事件处理程序:

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;
        }
    });
}

DOSOMETHING方法倾向于抛出;)@grzenio,这会导致排队等待挂锁释放吗?@AidanO,你只需要锁来确保检查和分配或运行是原子化的,不会有任何真正的等待。我已经读了你的问题很多次了,根本不清楚你想做什么。你能提供一些背景资料或更完整的描述你的代码应该做什么吗?到目前为止,所有的答案通常存在于两个阵营中的一个阵营:1)你在使用多个线程,你在描述一个线程同步问题;2)你没有使用多个线程,你在描述一个方法可重入性问题。你能通过更新来澄清你的问题吗?根据我目前看到的情况,我假设是后者。是这样吗?@Juliet,对不起,不正确地使用“initialSetDone”而不是“running”混淆了这个问题@布莱恩,是的,这是一个重入问题。答案似乎表明我使用的方法是正确的,但是可以通过使用try-catch来改进。问题是,在第一个线程成功设置running=true之前,两个线程可以通过检查if(!running)。在本例中,您将看到两个线程同时运行DOSOMETHING。@Grzenio:True,但问题从未表明涉及的多个线程和方法的签名
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;
        }
    });
}