我有一个对象,一次只能由一个线程使用。例如,我的对象包含 3 个方法A
,B
并且C
如果线程访问该方法,我想锁定对象(所有方法/属性都被锁定)A
。
主要困难是我无法修改该对象的代码。我必须防止多线程访问我调用对象的地方。
我的第一个想法是使用单例模式,但我没能成功!
我有一个对象,一次只能由一个线程使用。例如,我的对象包含 3 个方法A
,B
并且C
如果线程访问该方法,我想锁定对象(所有方法/属性都被锁定)A
。
主要困难是我无法修改该对象的代码。我必须防止多线程访问我调用对象的地方。
我的第一个想法是使用单例模式,但我没能成功!
如果您无法更改对象的代码,则必须处理对象外部的锁定。例如,您可以将其封装在另一个类中(可能将其隐藏在接口后面),并让该包装类应用同步:
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(); } }
}
单例模式在这里并不合适——它确保对象只有一个实例,但并不规定如何使用它。
代码的线程安全必须在该代码中定义。也就是说,如果您无法修改对象的代码,您将无法使其正确地成为线程安全的。但是,有一个解决方法:您可以将对象包装在您创建的新类中,并确保您的新对象是线程安全的。通过为不安全对象的方法公开线程安全包装器,您可以确保以您希望的方式访问它。
最简单的方法是使用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();
}
}
}
OP没有指定,但如果他的场景包括他需要在多个客户端调用中保持对象锁定的可能性(例如,他需要从客户端调用函数A,然后根据结果调用函数B或C,将对象一直锁定到其他线程)你需要实现一些不同的,例如:
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 释放锁为止。
EDIT: Added auto-obtaining a lock in the DoSomething and DoSomethingElse methods allowing a thread to also obtain a single-use lock if directly calling these methods without first obtaining a lock via the getLock method. It should be noted, however, that if the lock is obtained this way, it only lasts for the single method call.
假设您不能只为每个线程创建一个对象,另一种方法是再增加一个线程来调用非线程安全对象的方法,然后将调用请求排队到该线程。通常,线程在对非线程安全对象执行了请求的操作后,应该触发队列请求中提供的“OnCompletion”回调。
然后异步执行操作,但您可以通过将请求排队并等待回调发出的事件来进行同步调用。
..只是另一种比简单锁定包装对象更灵活的可能性。