一尘不染

使用获取/设置的C#线程安全

c#

这是C#的详细问题。

假设我有一个带有对象的类,并且该对象受锁保护:

Object mLock = new Object();
MyObject property;
public MyObject MyProperty {
    get {
         return property;
    }
    set { 
         property = value; 
    }
}

我希望轮询线程能够查询该属性。我还希望线程偶尔更新该对象的属性,有时用户可以更新该属性,并且用户希望能够看到该属性。

以下代码是否可以正确锁定数据?

Object mLock = new Object();
MyObject property;
public MyObject MyProperty {
    get {
         lock (mLock){
             return property;
         }
    }
    set { 
         lock (mLock){
              property = value; 
         }
    }
}

“适当”是指我想打电话给我

MyProperty.Field1 = 2;

或其他什么,我进行更新时会锁定该字段吗?是由equals运算符在’get’函数的作用域内完成的设置,还是先完成’get’函数(并因此锁定),然后再调用该设置,然后调用’set’,从而绕过锁?

编辑:既然这显然不会解决问题,那会怎样?我需要做类似的事情吗?

Object mLock = new Object();
MyObject property;
public MyObject MyProperty {
    get {
         MyObject tmp = null;
         lock (mLock){
             tmp = property.Clone();
         }
         return tmp;
    }
    set { 
         lock (mLock){
              property = value; 
         }
    }
}

这或多或少只是确保我只能访问一个副本,这意味着如果我要让两个线程同时调用一个“
get”,则它们每个都将从Field1的相同值开始(对吗?)。有没有办法对有意义的属性进行读写锁定?还是我应该约束自己锁定函数的各个部分,而不是锁定数据本身?

Just so that this example makes sense: MyObject is a device driver that
returns status asynchronously. I send it commands via a serial port, and then
the device responds to those commands in its own sweet time. Right now, I have
a thread that polls it for its status (“Are you still there? Can you accept
commands?”), a thread that waits for responses on the serial port (“Just got
status string 2, everything’s all good”), and then the UI thread which takes
in other commands (“User wants you to do this thing.”) and posts the responses
from the driver (“I’ve just done the thing, now update the UI with that”).
That’s why I want to lock on the object itself, rather than the fields of the
object; that would be a huge number of locks, a, and b, not every device of
this class has the same behavior, just general behavior, so I’d have to code
lots of individual dialogs if I individualized the locks.


阅读 284

收藏
2020-05-19

共1个答案

一尘不染

不,您的代码不会锁定对从返回的对象的成员的访问MyProperty。它只会锁定MyProperty自己。

您的示例用法实际上是将两个操作合而为一,大致等效于此:

// object is locked and then immediately released in the MyProperty getter
MyObject o = MyProperty;

// this assignment isn't covered by a lock
o.Field1 = 2;

// the MyProperty setter is never even called in this example

简而言之-如果两个线程MyProperty同时访问,则getter将短暂阻塞第二个线程,直到将对象返回到第一个线程为止,
随后它将把对象也返回到第二个线程。然后,这两个线程将具有对该对象的完全的,未锁定的访问权限。

编辑以回答问题中的更多详细信息

我仍然不能100%地确定要实现的目标,但是如果您只想对对象进行原子访问,那么您是否无法将调用代码锁定在对象本身上呢?

// quick and dirty example
// there's almost certainly a better/cleaner way to do this
lock (MyProperty)
{
    // other threads can't lock the object while you're in here
    MyProperty.Field1 = 2;
    // do more stuff if you like, the object is all yours
}
// now the object is up-for-grabs again

这不是理想的方法,但是只要所有对对象的访问都包含在lock (MyProperty)各个节中,则此方法将是线程安全的。

2020-05-19