Skip to main content
 首页 » 编程设计

c#之传递给其他线程的对象上的锁会怎样

2024年02月03日30think

我不太确定如何表达这个,所以我只粘贴我的代码并提出问题:

private void remoteAction_JobStatusUpdated(JobStatus status) { 
    lock (status) { 
        status.LastUpdatedTime = DateTime.Now; 
        doForEachClient(c => c.OnJobStatusUpdated(status)); 
        OnJobStatusUpdated(status); 
    } 
} 
 
private void doForEachClient(Action<IRemoteClient> task) { 
    lock (clients) { 
        foreach (KeyValuePair<RemoteClientId, IRemoteClient> entry in clients) { 
            IRemoteClient clientProxy = entry.Value; 
            RemoteClientId clientId = entry.Key; 
            ThreadPool.QueueUserWorkItem(delegate { 
                try { 
                    task(clientProxy); 
#pragma warning disable 168 
                } catch (CommunicationException ex) { 
#pragma warning restore 168 
                    RemoveClient(clientId); 
                } 
            }); 
        } 
    } 
} 

假设修改 status 对象的任何其他代码将首先获取对其的锁定。

由于 status 对象一直传递到多个 ThreadPool 线程,并且对 ThreadPool.QueueUserWorkItem 的调用将在实际任务完成后,我是否确保向所有客户端发送相同的 status 对象?

换句话说,lock (status) 语句什么时候“过期”或导致它的锁被释放?

请您参考如下方法:

锁不会过期。当一个线程试图传递 lock 语句时,只有当没有其他线程在 lock block 中执行时,它才能执行此操作,该 block 锁定了 中使用的特定对象实例code>lock 语句。

在你的情况下,你似乎有一个主线程在执行。在它旋转在单独线程上执行的新任务之前,它将同时锁定 status 和 clients 实例。如果新线程中的任何代码想要获取 status 或 clients 上的锁,它将必须等到主线程通过离开 释放两个锁锁 block 。当 remoteAction_JobStatusUpdated 返回时会发生这种情况。

您将 status 对象传递给每个工作线程,它们都可以自由地对该对象做任何他们想做的事情。 lock (status) 语句决不会保护 status 实例。但是,如果任何线程尝试执行 lock (status),它们将阻塞,直到主线程释放锁。

使用两个独立的对象实例来加锁会导致死锁。假设一个线程执行以下代码:

lock (status) { 
  ... 
  lock (clients) { 
    ... 
  } 

另一个线程执行以下代码,其中以相反的顺序获取锁:

lock (clients) { 
  ... 
  lock (status) { 
    ... 
  } 

如果第一个线程设法首先获得状态,而第二个线程首先获得客户端锁定,则它们将陷入死锁,并且两个线程都将不再运行。

一般来说,我会建议您将共享状态封装在一个单独的类中,并确保线程安全地访问它:

class State { 
 
  readonly Object locker = new Object(); 
 
  public void ModifyState() { 
    lock (this.locker) { 
      ... 
    } 
  } 
 
  public String AccessState() { 
    lock (this.locker) { 
      ... 
      return ... 
    } 
  } 
 
} 

您还可以使用 [MethodImpl(MethodImpl.Synchronized)] 标记您的方法属性,但它有其缺陷,因为它会用 lock (this) 包围方法,这通常不被推荐。

如果您想更好地理解 lock 语句的幕后情况,您可以阅读 Safe Thread Synchronization MSDN 杂志中的文章。