C# WaitOne()将永远等待,即使触发了所有事件

C# WaitOne()将永远等待,即使触发了所有事件,c#,multithreading,waitone,C#,Multithreading,Waitone,支持线程创建4个单独的线程,并等待每个线程完成。每个线程都会休眠一段时间,只有当共享互斥对象没有被另一个线程占用时才会终止,然后通过一个事件发出它已完成的信号(这是我代码的简化版本,但在同一点失败) 但是发生的情况是,主线程在大多数情况下都会在WaitOne()中的一个等待,看起来是随机的 此外,我还必须注释掉代码的某些部分,因为这会导致更意外的行为(即,在每个线程完成后,主线程会跳回for子句并导致IndexOutOfBounds) 类线程 { 静态互斥CM; 静态列表共享列表; 静态手动复位

支持线程创建4个单独的线程,并等待每个线程完成。每个线程都会休眠一段时间,只有当共享互斥对象没有被另一个线程占用时才会终止,然后通过一个事件发出它已完成的信号(这是我代码的简化版本,但在同一点失败)

但是发生的情况是,主线程在大多数情况下都会在WaitOne()中的一个等待,看起来是随机的

此外,我还必须注释掉代码的某些部分,因为这会导致更意外的行为(即,在每个线程完成后,主线程会跳回for子句并导致IndexOutOfBounds)

类线程
{
静态互斥CM;
静态列表共享列表;
静态手动复位事件CEvent=新手动复位事件(假);
静态手动复位事件1=新手动复位事件(假);
静态手动复位事件2=新手动复位事件(假);
静态手动复位事件3=新手动复位事件(假);
静态手动复位事件4=新手动复位事件(假);
公共列表ThreadedMangaIndexCrawl(int-MaxThreads)
{
CM=新互斥(假);
SharedList=新列表();
ManualResetEvent[]evs=新的ManualResetEvent[4];
evs[0]=Event1;//t1的事件
evs[1]=Event2;//t2的事件
evs[2]=Event3;//t3的事件
evs[3]=Event4;//t4的事件
/*对于(int i=0;iMaxThreads)
{break;}
线程t=新线程(()=>this.StartIndexCrawling(1,i,i+1,evs[i]);
t、 Start();
}*/
int i=0;
线程t1=新线程(()=>this.StartIndexCrawling(1,i,i+1,evs[i]);
t1.Name=“Thread”+i;
t1.Start();
i++;
线程t2=新线程(()=>this.StartIndexCrawling(1,i,i+1,evs[i]);
t2.Name=“Thread”+i;
t2.Start();
i++;
threadt3=新线程(()=>this.StartIndexCrawling(1,i,i+1,evs[i]);
t3.Name=“Thread”+i;
t3.Start();
i++;
threadT4=新线程(()=>this.StartIndexCrawling(1,i,i+1,evs[i]);
t4.Name=“Thread”+i;
t4.开始();
/*foreach(evs中的var e)
{ 
e、 WaitOne();
}*/
evs[0].WaitOne();
evs[1].WaitOne();
evs[2].WaitOne();
evs[3].WaitOne();
返回共享列表;
}
无效开始索引爬网(整数目标、整数开始、整数结束、手动重置事件E)
{
睡眠(1000);
CM.WaitOne();
CM.ReleaseMutex();
E.Set();
}
}

任何帮助都会很好

最有可能的是,所有四个线程都将执行:

this.StartIndexCrawling(1, 3, 3 + 1, evs[4]);
这与闭包的使用有关。所有四个线程都将绑定到变量
i
,并在执行代码后使用它拥有的任何值(而不是创建
Thread
对象时的值)

如果所有四个线程使用相同的值,则代码不太可能工作。

请参阅Codo的答案。
以下是您应该如何解决此问题:

   int i = 0;
   Thread t1 = new Thread(() => this.StartIndexCrawling(1, 0, 1, Event1));
   t1.Name = "Thread" + i;
   t1.Start();
   i++;
   Thread t2 = new Thread(() => this.StartIndexCrawling(1, 1, 2, Event2));
   t2.Name = "Thread" + i;
   t2.Start();
   i++;
   Thread t3 = new Thread(() => this.StartIndexCrawling(1, 2, 3, Event3));
   t3.Name = "Thread" + i;
   t3.Start();
   i++;
   Thread t4 = new Thread(() => this.StartIndexCrawling(1, 3, 4, Event4));
   t4.Name = "Thread" + i;
   t4.Start();

您正在创建多个对象吗?静态成员对于一个非单一对象来说是相当危险的。请考虑使用这个逻辑来清理这个逻辑。@ RokEn:我认为任务不会清除执行流混乱,这只是另一个构造,用更多的EnAT助手/扩展来做异步工作。你的意思是不止一个“线程”吗?不,我只是做了一个例子,你似乎从来没有把一个
CM
互斥体切换到信号状态,所以这是一场比赛。也看看这篇文章,我会被诅咒的。那是我认为是原因的最后一件事。但是为什么线程使用当前的i而不是通过参数得到的i来执行它们自己呢//edit在
StartIndexCrawling
中发布后刚刚看到链接,您有一个值的本地副本。但是线程最顶层的代码是由闭包this.StartIndexCrawling(1,i,i+1,evs[i])定义的匿名函数。此代码表示共享变量
i
。闭包就是这样工作的。
   int i = 0;
   Thread t1 = new Thread(() => this.StartIndexCrawling(1, 0, 1, Event1));
   t1.Name = "Thread" + i;
   t1.Start();
   i++;
   Thread t2 = new Thread(() => this.StartIndexCrawling(1, 1, 2, Event2));
   t2.Name = "Thread" + i;
   t2.Start();
   i++;
   Thread t3 = new Thread(() => this.StartIndexCrawling(1, 2, 3, Event3));
   t3.Name = "Thread" + i;
   t3.Start();
   i++;
   Thread t4 = new Thread(() => this.StartIndexCrawling(1, 3, 4, Event4));
   t4.Name = "Thread" + i;
   t4.Start();