锁定传递给其他线程的对象会发生什么?

时间:2010-09-21 13:19:14

标签: c# multithreading threadpool

我不太清楚如何说出来,所以我只是粘贴我的代码并提出问题:

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的调用将在实际任务完成之前完成,我确保相同{ {1}}对象被发送给所有客户端?

换句话说,status语句何时“过期”或导致其锁被释放?

2 个答案:

答案 0 :(得分:5)

锁不会过期。当一个线程试图传递lock语句时,如果在lock状态中使用的特定对象实例上锁定的lock块内没有执行其他线程,则它只能执行该操作。

在您的情况下,似乎您正在执行主线程。它将锁定statusclients实例,然后再旋转在单独线程上执行的新任务。如果新线程中的任何代码想要获取statusclients上的锁,则必须等待主线程通过保留两个lock块来释放两个锁。当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语句幕后的内容,可以阅读MSDN杂志中的Safe Thread Synchronization文章。

答案 1 :(得分:0)

锁定肯定不会自己“过期”,锁定将一直有效,直到lock(..){}语句的右括号。