C# 使用 lock(this) 说明死锁的示例代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/894037/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sample code to illustrate a deadlock by using lock(this)
提问by MedicineMan
I've read several articles and posts that say that lock(this)
, lock(typeof(MyType))
, lock("a string")
are all bad practice because another thread could lock on the same key and cause a deadlock. In order to understand this problem, I was trying to create some sample code to illustrate the deadlock but have been unable to wrap my head around this.
我读过几篇文章和帖子是说lock(this)
,lock(typeof(MyType))
,lock("a string")
都是不好的做法,因为另一个线程可以锁定相同的密钥和导致死锁。为了理解这个问题,我试图创建一些示例代码来说明死锁,但一直无法解决这个问题。
Can someone write a concise bit of code that illustrates this classic problem? Please keep it short, I can digest code in smaller chunks only.
有人可以写一段简洁的代码来说明这个经典问题吗?请保持简短,我只能以较小的块消化代码。
Edit:I think lassevk sums it up well; that the real problem is that you have lost control over your locks. Once that happens, you cannot control the order the locks are called, and you are allowing a potential deadlock situation.
编辑:我认为 lassevk 总结得很好;真正的问题是您失去了对锁的控制。一旦发生这种情况,您将无法控制调用锁的顺序,并且您正在允许潜在的死锁情况。
lock(this)
, lock(typeof(MyType))
, etc all are situations where you have chosen a lock that is impossible to control.
lock(this)
、lock(typeof(MyType))
等都是您选择了无法控制的锁的情况。
采纳答案by jalf
A deadlock will only occur if you have more than one lock. You need a situation where both threads hold a resource that the other needs (which means there has to be a least two resources, and the two threads have to attempt to acquire them in a different order)
只有当你有多个锁时才会发生死锁。您需要一种情况,其中两个线程都持有另一个需要的资源(这意味着必须至少有两个资源,并且两个线程必须尝试以不同的顺序获取它们)
So a simple example:
所以一个简单的例子:
// thread 1
lock(typeof(int)) {
Thread.Sleep(1000);
lock(typeof(float)) {
Console.WriteLine("Thread 1 got both locks");
}
}
// thread 2
lock(typeof(float)) {
Thread.Sleep(1000);
lock(typeof(int)) {
Console.WriteLine("Thread 2 got both locks");
}
}
Assuming both threads are started within a second of each others, they will both have time to grab the first lock before anyone gets to the inner lock. Without the Sleep() call, one of the threads would most likely have time to get and release both locks before the other thread even got started.
假设两个线程在彼此的一秒内启动,在任何人进入内部锁之前,它们都有时间获取第一个锁。如果没有 Sleep() 调用,其中一个线程很可能有时间在另一个线程启动之前获取和释放这两个锁。
回答by Lasse V. Karlsen
Sure, here you go.
喏,给你。
Note that the common example for a deadlock is when you acquire multiple locks, and two or more threads end up waiting for each other.
请注意,死锁的常见示例是当您获得多个锁时,两个或多个线程最终相互等待。
For instance, two threads that locks like this:
例如,像这样锁定的两个线程:
Thread 1 Thread 2
Lock "A" Lock "B"
Lock "B" Lock "A" <-- both threads will stop dead here
waiting for the lock to be come
available.
However, in this example I didn't bother with that, I just let one thread lock indefinitely. You really don't want to loose control over your locks, so while this is a contrived example, the fact that the background thread can completely block the main thread like this, is bad.
然而,在这个例子中,我并没有为此烦恼,我只是让一个线程无限期地锁定。你真的不想放松对你的锁的控制,所以虽然这是一个人为的例子,但后台线程可以像这样完全阻塞主线程的事实很糟糕。
using System;
using System.Threading;
namespace ConsoleApplication7
{
public class Program
{
public static void Main(string[] args)
{
LockableClass lockable = new LockableClass();
new Thread(new ParameterizedThreadStart(BackgroundMethod)).Start(lockable);
Thread.Sleep(500);
Console.Out.WriteLine("calling Reset");
lockable.Reset();
}
private static void BackgroundMethod(Object lockable)
{
lock (lockable)
{
Console.Out.WriteLine("background thread got lock now");
Thread.Sleep(Timeout.Infinite);
}
}
}
public class LockableClass
{
public Int32 Value1 { get; set; }
public Int32 Value2 { get; set; }
public void Reset()
{
Console.Out.WriteLine("attempting to lock on object");
lock (this)
{
Console.Out.WriteLine("main thread got lock now");
Value1 = 0;
Value2 = 0;
}
}
}
}
回答by plinth
The problem is that lock("a string") is locking on a singleton. This means that other objects that use the same lock could be an infinite wait.
问题是 lock("a string") 锁定在单例上。这意味着使用相同锁的其他对象可能会无限等待。
for example:
例如:
using System;
using System.Threading;
namespace ThreadLock
{
class Program
{
static void Main(string[] args)
{
lock ("my lock")
{
ManualResetEvent evt = new ManualResetEvent(false);
WorkerObject worker = new WorkerObject(evt);
Thread t = new Thread(new ThreadStart(worker.Work));
t.Start();
evt.WaitOne();
}
}
}
class WorkerObject
{
private ManualResetEvent _evt;
public WorkerObject(ManualResetEvent evt)
{
_evt = evt;
}
public void Work()
{
lock ("my lock")
{
Console.WriteLine("worked.");
_evt.Set();
}
}
}
}
In this case, the calling code creates a lock on a string then makes a worker object. The worker object in Work() locks on the same string, which is a singleton in C#. It ends up in deadlock because the caller owns the lock and is waiting for a signal which will never come.
在这种情况下,调用代码在字符串上创建一个锁,然后创建一个工作对象。Work() 中的 worker 对象锁定在同一个字符串上,这在 C# 中是单例。它最终陷入死锁,因为调用者拥有锁并且正在等待一个永远不会到来的信号。
回答by JP Alioto
This is pretty standard bad-ness. Grabing the locks out of order and then sleeping with the lock. Two bad things to do. :)
这是非常标准的坏事。乱抓锁,然后带着锁睡觉。要做两件坏事。:)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DeadLock
{
public class Program
{
static void Main(string[] args)
{
var ddt = new DontDoThat();
ddt.Go();
}
}
public class DontDoThat
{
private int _badSharedState = 0;
private readonly object _lock1 = new object();
private readonly object _lock2 = new object();
public void Go()
{
new Thread(BadGuy1).Start();
new Thread(BadGuy2).Start();
Console.WriteLine("Leaving Go!");
}
public void BadGuy1()
{
lock (_lock1)
{
Thread.Sleep(100); // yeild with the lock is bad
lock (_lock2)
{
_badSharedState++;
Console.Write("From Bad Guy #1: {0})", _badSharedState );
}
}
}
public void BadGuy2()
{
lock (_lock2)
{
lock (_lock1)
{
_badSharedState++;
Console.Write("From Bad Guy #2: {0})", _badSharedState);
}
}
}
}
}
回答by Maghis
The idea is that you should never lock on something you cannot control who has access to.
这个想法是你永远不应该锁定你无法控制谁可以访问的东西。
Type objects are singletons visible to every .net piece of code and you cannot control who locks on your "this" object from the outside.
类型对象是每个 .net 代码段可见的单例,您无法从外部控制谁锁定了您的“this”对象。
Same thing is for strings: since strings are immutable, the framework keeps just one instance of "hard coded" strings and puts them in a pool (the string is said to be interned), if you write two times in your code the string "hello", you will always get the same abject.
字符串也是如此:由于字符串是不可变的,因此框架只保留一个“硬编码”字符串实例并将它们放入池中(该字符串被称为是实习的),如果您在代码中写入两次字符串“你好”,你将永远得到同样的卑鄙。
Consider the following example: you wrote just Thread1 in your super private call, while Thread2 is called by some library you are using in a background thread...
考虑以下示例:您在超级私有调用中只编写了 Thread1,而 Thread2 由您在后台线程中使用的某个库调用...
void Thread1()
{
lock (typeof(int))
{
Thread.Sleep(1000);
lock (typeof(long))
// do something
}
}
void Thread2()
{
lock (typeof(long))
{
Thread.Sleep(1000);
lock (typeof(int))
// do something
}
}
回答by lebovskiy
class Character
{
public Character Other;
public string Name;
private object locker = new object();
public Character(string name)
{
Name = name;
}
public void Go()
{
lock (locker)
{
Thread.Sleep(1000);
Console.WriteLine("go in {0}", Name);
Other.Go();
}
}
}
class Program
{
static void Main(string[] args)
{
Character a = new Character("A");
Character b = new Character("B");
a.Other = b;
b.Other = a;
new Thread(a.Go).Start();
b.Go();
Console.ReadLine();
}
}