在调用开始之前,迭代中的值会发生变化

我在我的应用程序中有以下代码。

MyEventHandler handler = null; //Declare the handler foreach (string pname in group) { handler = getHandler(pname); //Get the handler if(handler == null) { throw new KeyNotFoundException("No user " + pname + " could be found"); } //invoke the handler handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null); } 

所以我得到了处理程序并调用了BeginInvoke方法。 但是在调用BeginInvoke之前,它会进入下一次迭代并且处理程序值会发生变化。 所以BeginInvoke正在涉及这个新的处理程序。

希望你明白我的观点。 那我怎么能消除这个问题呢? 我不想在BeginInvoke之后调用睡眠,因为我觉得这是一个时间的浪费。

有任何想法吗?

Update1我很确定在调用BeginInvoke()之前处理程序对象会被更改。 我猜BeginInvoke需要一些时间来创建一个单独的线程来调用另一个函数。

Update2此代码在WCF服务中,客户端调用一个函数,该函数依次使用此函数。 我为每个客户端分别存储在我的服务器中的处理程序。 WCF服务具有双工合同,为客户端分隔会话。 我看到执行此函数后,同一个用户被调用两次。 但是我提出了一个断点并对其进行调试(这使得BeginInvoke有必要的时间来调用该函数)它的工作“完美”。 我非常确定我在线程中也遇到了这个问题,我在循环中创建了多个线程。 如果线程委托具有参数a,b,c,并且如果在下一次迭代开始时更改它,则会发生相同的行为。 我之前有多少人遇到过这个问题。 如果我放一个Sleep()或者如果我复制一个处理程序并使用copy调用它就可以了。

UPDATE3

好的,我现在已经测试过了。 我刚刚添加了Thread.Sleep(),如下所示。

 chatTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null); Thread.Sleep(500); 

它的工作就像一个魅力。 有什么想法吗?

更新4

我已经创建了一个演示问题的线程示例, 我已将其上传到此处 。 我希望解决这个问题也能解决我的问题。 请检查样品。

我不明白为什么会发生这种情况 – 您发布的代码无法重现您描述的行为。 完全合理的是,BeginInvoke调用可能不会立即执行任何操作,并且在您实际看到该调用执行任何操作之前可能会发生下一次迭代 – 因为它将排队等待工作线程处理。

这并不意味着调用了不同的处理程序 – 一旦调用了BeginInvoke,就会捕获要调用的处理程序,因此后续局部变量是否会发生变化无关紧要

另外 – 为什么你在这里锁? 除非多个线程在同一个可枚举的同时进行此处理(在这种情况下你为什么要这样做),我看不出你为什么要锁定的任何理由。

我还会说,如果您通过调试器中看到的内容来判断这种行为,那么您不必担心 – 通过执行此操作,调试器将获得“有趣”的结果,以及混合中的多个线程在’Threads’调试器窗口中切换线程很重要。

问题是 – 您的计划实际上是否符合您的期望? 如果是这样,但你在调试时看到了这种奇怪的行为 – 那就完全正常了。

正如一些评论所说 – 您发布的代码不能正是产生问题的原因。 例如,如果’handler’是在多个线程之间共享的局部变量然后执行此迭代,那么,是的,你可以得到这样的东西。 但是方法的局部变量只能由当前在该方法中的相同线程修改(实际上是读取); 该规则的唯一例外是,如果handler引用随后作为ref传递给另一个线程方法。

好的,在你第四次编辑之后,你给我们提供了一个例子,它表明了一个问题,当然,但不是你要求帮助的问题

你在问题中说的是:

  • 我在委托变量上使用BeginInvoke,然后更改变量,并以某种方式调用我的委托两次

您在发布的代码中展示的是:

  • 我在匿名方法中捕获循环变量,并以某种方式使用错误的变量值

这些都不是同样的问题!

你发布的代码行为不端的原因是有问题的代码实际上看起来像这样:

 int i; for (i = 0; i < 10; i++) ... create delegate, capture i, spawn thread 

在这里,您将为所有线程捕获相同的变量。 如果一个线程在循环更改变量之前没有开始执行,那么是的,您将看到该变量的“不正确的值”。

但是,如果您更改代码如下:

 for (int i = 0; i < 10; i++) { int j = i; ThreadStart threadStartObj = new ThreadStart( delegate { PrintValueThreadFunction(j, j); }); ^ | +-- use j instead of i here 

然后,您将为每个线程捕获一个“新”变量,该变量不会被更改。

所以,问题仍然存在。 这是你遇到的问题吗? 如果是这样,那么下次羞辱你不要简化问题 。 你在浪费人们的时间,尤其是你自己的时间。 如果你开始发布上面的代码,你会在几分钟内得到一个答案(或一个重复的问题指向现有的答案,有很多)。

如果这不是您遇到的问题,那么您仍然遇到一个事件处理程序的问题,就像在不止一次调用的原始代码中一样,返回并生成一个更好的示例项目。

我认为你还有其他一些问题…在下一次迭代后无法调用handler.BeginInvoke,你仍然在同一个线程中……