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

时间:2010-07-01 09:52:51

标签: c# .net scope asynchronous

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

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

我创建了一个演示问题的线程示例和i've uploaded it here。我希望解决这个问题也能解决我的问题。请检查样品。

3 个答案:

答案 0 :(得分:5)

好的,在您进行第四次编辑后,您向我们提供了一个示例,该示例表明存在问题,当然不是您要求帮助的问题

你在问题​​中说的是:

  • 我在委托变量上使用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

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

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

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

答案 1 :(得分:1)

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

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

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

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

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

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

答案 2 :(得分:0)

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