我有一个对象,一次只能由一个Thread使用。
例如,我的对象包含3个方法A
,B
和C
,如果线程访问方法{{1},我想锁定对象(所有方法/属性都被锁定) }。
主要的困难是我无法修改该对象的代码。我必须阻止多线程访问我调用对象的位置。
我的第一个想法是使用单例模式,但我没有设法让它工作!
答案 0 :(得分:10)
如果无法更改对象的代码,则必须处理对象对象的锁定。例如,您可以将其封装在另一个类中(可能将其隐藏在接口后面),并让该包装类应用同步:
public class Foo {
private readonly YourType tail;
private readonly object syncLock = new object();
public Foo(YourType tail) {this.tail = tail;}
public A() { lock(syncLock) { tail.A(); } }
public B() { lock(syncLock) { tail.B(); } }
public C() { lock(syncLock) { tail.C(); } }
}
答案 1 :(得分:5)
Singleton模式在这里不是正确的 - 它确保只有一个对象的实例,但没有规定如何使用它。
代码的线程安全性必须在该代码中定义。也就是说,如果您无法修改对象的代码,则无法使其正确地保证线程安全。但是,有一种解决方法:您可以将对象包装在您创建的新类中,并确保新对象 是线程安全的。通过为您不安全的对象的方法公开线程安全的包装器,您可以确保以您希望的方式访问它。
最简单的方法是使用lock
关键字。这样的事情可能有用:
public class ThreadSafeThing
{
private UnsafeThing _thing = new UnsafeThing();
private object _syncRoot = new object();
public void DoSomething() // this is your thread-safe version of Thing.DoSomething
{
lock (_syncRoot)
{
_thing.DoSomething();
}
}
}
答案 2 :(得分:1)
public static class ThreadSafeThing {
private static UnsafeThing _thing = new UnsafeThing();
private static readonly object _lock = new object();
public static void getLock() {
Monitor.Enter(_lock);
}
public static void releaseLock() {
Monitor.Exit(_lock);
}
// this is your thread-safe version of Thing.DoSomething
public static bool DoSomething() {
try {
Monitor.Enter(_lock);
return _thing.DoSomething();
}
finally {
Monitor.Exit(_lock);
}
}
// this is your thread-safe version of Thing.DoSomethingElse
public static void DoSomethingElse() {
try {
Monitor.Enter(_lock);
return _thing.DoSomethingElse();
}
finally {
Monitor.Exit(_lock);
}
}
}
从客户来电这样......
try {
ThreadSafeThing.getLock();
if (ThreadSafeThing.DoSomething()) {
ThreadSafeThing.DoSomethingElse();
}
}
finally {
// This must be called no matter what happens
ThreadSafeThing.releaseLock();
}
这里的主要区别是客户端负责获取锁并在完成后释放它。这允许在保持锁定的同时调用对象上的多个函数。所有其他线程将阻止getLock调用,直到使用releaseLock释放锁。
编辑:在DoSomething和DoSomethingElse方法中添加了自动获取锁,允许线程在没有首先通过getLock方法获得锁定的情况下直接调用这些方法时也获得一次性锁。然而,应该注意的是,如果以这种方式获得锁定,则它仅持续单个方法调用。
答案 3 :(得分:0)
假设您不能仅为每个线程创建一个对象,另一种方法是再引发一个线程来调用非线程安全对象的方法,然后将调用请求排入该一个线程。通常,线程应该在对非线程安全对象执行requsted操作之后触发排队请求中提供的'OnCompletion'回调。
然后异步执行操作,但您可以通过排队请求然后等待回调发出信号的事件来进行同步调用。
..只是另一种可能性比包装器对象中的简单锁定更灵活。