使整个方法线程安全的最简单方法?
关于多线程编程似乎有很多东西需要学习,而且都有点令人生畏。
对于我当前的需求,我只想防止在完成之前从另一个线程再次调用一个方法,我的问题是:
这是创建方法的适当(安全)方法吗?线程安全?
class Foo
{
bool doingWork;
void DoWork()
{
if (doingWork) // <- sophistocated thread-safety
return; // <-
doingWork = true;
try
{
[do work here]
}
finally
{
doingWork = false;
}
}
}
如果这还不够,实现这一点的最简单方法是什么?
编辑:有关该场景的更多信息:
只有一个 Foo 实例
将从 ThreadPool 调用 Foo.DoWork()已过去的线程 System.Timers.Timer 的事件。
通常 Foo.DoWork() 会在下一次之前完成亿万次 被调用,但我想为它运行很长时间的微小机会进行编码, 并在完成之前再次被调用。
(我也不够聪明,无法确定这个问题是否可以被标记为与语言无关,所以我没有。开明的读者,如果适用,请随意这样做。)
There seems to be a lot to learn about multithreaded programming and it's all a bit intimidating.
For my current needs, I just want to protect against a method being called again from another thread before it finishes, and my question is:
Is this an adequate (safe) way to make a method thread-safe?
class Foo
{
bool doingWork;
void DoWork()
{
if (doingWork) // <- sophistocated thread-safety
return; // <-
doingWork = true;
try
{
[do work here]
}
finally
{
doingWork = false;
}
}
}
If that isn't sufficient, what is the simplest way to achieve this?
EDIT: More info about the scenario:
There is only one instance of Foo
Foo.DoWork() will be called from a ThreadPool thread on the Elapsed
event of a System.Timers.Timer.Normally Foo.DoWork() will finish eons before the next time it's
called, but I want to code for the slim chance that it will run long,
and get called again before finishing.
(I'm also not smart enough to be sure if this question could be tagged language-agnostic, so I haven't. Enlightened readers, feel free to do so if applicable.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的代码不是线程安全的。您应该使用
lock
关键字。在您当前的代码中:
当下一个线程通过时,它也会进入该函数。
这就是为什么应该使用
lock
结构。它基本上与您的代码执行相同的操作,但没有线程在中间被中断的风险:请注意,此代码的语义与原始代码有些不同。此代码将导致第二个线程进入等待,然后执行工作。您的原始代码使第二个线程中止。为了更接近原始代码,不能使用 C#
lock
语句。必须直接使用底层Monitor
构造:Your code is not thread safe. You should use the
lock
keyword instead.In your current code:
When the next thread comes through, it will also enter the function.
This is why the
lock
construct should be used. It basically does the same as your code, but without the risk for a thread being interrupted in the middle:Note that this code has somewhat different semantics than your original. This code will cause the second thread entering to wait and then do the work. Your original code made the second thread just abort. To come closer to your original code, the C#
lock
statement cannot be used. The underlyingMonitor
construct has to be used directly:可重入与多线程无关。
可重入方法是一种最终可以在同一线程上从其自身内部调用的方法。
例如,如果一个方法引发一个事件,并且处理该事件的客户端代码在事件处理程序内再次调用该方法,则该方法是可重入的。
保护该方法免于重入意味着确保如果您从其内部调用它,它不会执行任何操作或抛出异常。
只要所有内容都在同一线程上,您的代码就不会在同一对象实例中重入。
除非
[do work here]
能够运行外部代码(例如,通过引发事件,或通过从其他东西调用委托或方法),否则它一开始就不是可重入的。您编辑的问题表明这整个部分与您无关。
无论如何你应该读一下它。
您可能(编辑:正在)寻找排他性 - 确保该方法在被多个线程同时调用时不会同时运行两次。
您的代码不是唯一的。如果两个线程同时运行该方法,并且它们都同时运行
if
语句,那么它们都将通过if
,然后都设置doingWork< /code> 标志,并且都将运行整个方法。
为此,请使用
lock
关键字。Re-entrancy has nothing to do with multi-threading.
A re-entrant method is a method that can end up being called from within itself, on the same thread.
For example, if a method raises an event, and the client code that handles that event calls the method again inside the event handler, that method is re-entrant.
Protecting that method from re-entrancy means making sure that if you call it from inside itself, it will either not do anyhting or throw an exception.
Your code is protected from re-entrancy within the same object instance, as long as everything is on the same thread.
Unless
[do work here]
is capable of running external code (eg, by raising an event, or by calling a delegate or method from something else), it isn't re-entrant in the first place.Your edited question indicates that this entire section is irrelevant to you.
You should probably read it anyway.
You may be (EDIT: are) looking for exclusivity – ensuring that the method will not run twice at once if called by multiple threads simultaneously.
Your code is not exclusive. If two threads run the method at once, and they both run the
if
statement at once, they will both get past theif
, then both set thedoingWork
flag, and will both run the entire method.To do that, use the
lock
keyword.如果你想要简单代码并且不太关心性能,它可以像
}
一样简单如果你想封装一点锁定,你可以创建一个线程安全的属性,如下所示:
现在你可以使用它代替了 field ,但是这会导致锁定花费更多时间,导致锁使用次数增加。
或者您可以使用完整的栅栏方法(来自伟大的线程书籍 Joseph Albahari 在线线程 )
他指出,完整的栅栏比锁定语句快 2 倍。在某些情况下,您可以通过删除不必要的 MemoryBarrier() 调用来提高性能,但使用
lock
更简单、更清晰且不易出错。我相信这也可以使用基于 int 的 doingWork 字段的 Interlocked 类来完成。
if you want easy code and dont care about performance too much it can be as easy as
}
If you want to incapsulate locking a little you can create a property that is thread safe like this:
Now you can use it instead field , however it will result in more time spent for locking cause number of lock uses increases.
Or you can use full fence approach ( from great threading book Joseph Albahari online threading )
He states that full fence is 2x faster than lock statement. In some cases you can improve performance by removing unneeded calls to MemoryBarrier(), but using
lock
is simple, more clear and less error prone.I believe this can also be done using
Interlocked
class around int based doingWork field.http://msdn.microsoft.com/en-us/library /system.threading.barrier.aspx
可能想考虑使用屏障,它会为您完成所有工作。这是控制可重入代码的标准方法。还允许您控制一次执行该工作的线程数量(如果允许超过 1 个)。
http://msdn.microsoft.com/en-us/library/system.threading.barrier.aspx
Might want to look into using barrier instead, it does all of the work for you. It's the standard way of controlling reentrant code. Also allows you to control the amount of threads doing that work at once (if you allow more than 1).