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

发布于 2024-09-24 04:16:56 字数 1178 浏览 6 评论 0原文

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

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) 语句何时“过期”或导致其锁被释放?

I'm not quite sure how to word this, so I'll just paste my code and ask the question:

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);
                }
            });
        }
    }
}

Assume that any other code which modifies the status object will acquire a lock on it first.

Since the status object is passed all the way through to multiple ThreadPool threads, and the call to ThreadPool.QueueUserWorkItem will complete before the actual tasks complete, am I ensuring that the same status object gets sent to all clients?

Put another way, when does the lock (status) statement "expire" or cause its lock to be released?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无所的.畏惧 2024-10-01 04:16:56

锁不会过期。当一个线程尝试传递 lock 语句时,只有在 lock 块内没有其他线程正在执行该语句时,该块才能执行此操作,该块对 lock 中使用的特定对象实例具有锁定。代码>锁定语句。

在你的情况下,你似乎有一个正在执行的主线程。在旋转在单独线程上执行的新任务之前,它将锁定 statusclients 实例。如果新线程中的任何代码想要获取 statusclients 上的锁,则必须等待,直到主线程通过保留两个 来释放这两个锁。锁定块。当 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)] 属性,但它有其缺陷,因为它会用 锁包围该方法(this) 通常不推荐这样做。

如果您想更好地了解 lock 语句的幕后情况,您可以阅读 MSDN 杂志中的安全线程同步文章。

Locks don't expire. When a thread tries to pass the lock statement it can only do it if no other thread is executing inside a lock block having a lock on that particular object instance used in the lock statemement.

In your case it seems that you have a main thread executing. It will lock both the status and the clients instances before it spins of new tasks that are executed on seperate threads. If any code in the new threads want to acquire a lock on either status or clients it will have to wait until the main thread has released both locks by leaving both lock blocks. That happens when remoteAction_JobStatusUpdated returns.

You pass the status object to each worker thread and they are all free to do whatever they want to do with that object. The statement lock (status) in no way protects the status instance. However, if any of the threads tries to execute lock (status) they will block until the main thread releases the lock.

Using two separate object instances to lock can lead to deadlock. Assume one thread executes the following code:

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

}

Another thread executes the following code where the locks are acquired in the reverse sequence:

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

}

If the first thread manages to get the status first and the second the clients lock first they are deadlocked and both threads will no longer run.

In general I would advice you to encapsulate your shared state in a separate class and make access to it thread safe:

class State {

  readonly Object locker = new Object();

  public void ModifyState() {
    lock (this.locker) {
      ...
    }
  }

  public String AccessState() {
    lock (this.locker) {
      ...
      return ...
    }
  }

}

You can also mark you methods with the [MethodImpl(MethodImpl.Synchronized)] attribute, but it has its pitfalls as it will surround the method with a lock (this) which in general isn't recommended.

If you want to better understand what is going on behind the scenes of the lock statement you can read the Safe Thread Synchronization article in MSDN Magazine.

柳若烟 2024-10-01 04:16:56

锁当然不会自行“过期”,锁在 lock(..){} 语句的右大括号之前一直有效。

The locks certainly don't "expire" on their own, the lock will be valid until the closing brace of the lock(..){} statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文