繁忙等待线程

基本上,我需要忙着等到网页上出现一些html。 我已经创建了以下代码来忙着等我:

public void ExecuteBusyWaitThreads() { foreach (Canidate canidate in allCanidates) { Thread newThread = new Thread(delegate() { BusyWait(canidate); }); newThread.Start(); } } public bool BusyWait(Canidate canidate) { //hit that url, and wait for the claim all button to appear string page = null; while (found == false) { HttpWebRequest request = Canidate.GetHTTPRequest(canidate.URL); //make sure we add the authentication cookes to the request request = Canidate.AddCookiesToRequest(request, canidate.GetCookies()); page = new Canidate().GetPage(request); if (page.ToLower().Contains("claim all")) { found = true; NotifyAllThreads(); } } return true; } 

所以,如果我有8个canidates ,它将产生8个线程,每个线程都在寻找claim all显示在网页上。 found是一个全局变量。 一旦其中一个线程发现claim all ,他们都应该保释。

我对这种方法有几个问题。 首先,这是一个很好的方法。 其次,每个线程都会获得忙等待function的“副本”。 我的意思是,一个线程可以抢占另一个线程并更改该函数中的数据,或者它们每个都获得函数内声明的变量的副本。 请注意,这两个函数都在同一个对象中。

在我回答你的问题之前,我必须指出你已经做出了关闭循环变量的恶劣行为。

首先,这是一个很好的方法。

不,不是真的。 任意创建线程通常不是一个好主意。 最好使用线程池技术。 这可以使用ThreadPool.QueueUserWorkItemTask类完成。

其次,每个线程都会获得忙等待function的“副本”。 我的意思是,一个线程可以抢占另一个线程并更改该函数中的数据,或者它们每个都获得函数内声明的变量的副本。

BusyWait每个运行实例BusyWait将获得自己的所有局部变量的副本(即pagerequest )。 由于在非本地范围内声明(大概是无论如何),因此它将在所有正在运行的BusyWait实例之间共享。 因此,您对当前的读取和写入操作不是线程安全的,因为没有适当的同步机制。

其次,每个线程都会获得忙等待function的“副本”

每个线程将使用它自己的堆栈空间执行该函数,这意味着函数中本地的任何变量都将属于它正在运行的线程。 如果你有一个全局变量,就像你在函数内部改变你found变量一样,你需要设置一个同步机制,这样就不会同时从多个线程中访问它,因为这会导致很难找到bug和a很多你不想想象的恐怖经历!

所有线程都获得自己的局部变量副本(在本例中只有string page )。

您的共享found变量应该声明为volatile

这是一种罕见的情况,对Thread.Sleep()的调用可能会有所帮助。 在对同一站点的呼叫之间插入一点呼吸。

每个线程都使用自己的变量副本运行。

但是我会修改我的appoarch。 使用找到的变量不是线程安全的。 有可能一次发现多个线程正在发生变化。 一个线程也可能正在读取它而另一个线程正在编写它。 [lock][1]可以避免这种情况。

解决此问题的更好方法是使用EventWaitHandle 。 这样你就不必担心锁定,你可以建立一个睡眠或超时,所以如果’claim-all’没有出现你的线程将不会比你想要的更长。

 internal class ExampleOnExecute { private static EventWaitHandle _stopEvent; public static EventWaitHandle StopEvent { get { return _stopEvent ?? (_stopEvent = new EventWaitHandle(false, EventResetMode.ManualReset)); } } public static void SpinOffThreads(IEnumerable someCollection) { foreach(var item in someCollection) { // You probably do not want to manualy create a thread since these ideally would be small workers // and action BeingInvoke runs in the ThreadPool Action process = BusyWait; process.BeginInvoke(item, null, null); } } private static void BusyWait(object obj) { // You can wait for however long you like or 0 is not waiting at all const int sleepAmount = 1; // Blocks the current thread until the current instance receives a signal, using // a System.TimeSpan to specify the time interval. // // Parameters: // timeout: // A System.TimeSpan that represents the number of milliseconds to wait, or // a System.TimeSpan that represents -1 milliseconds to wait indefinitely. // // Returns: // true if the current instance receives a signal; otherwise, false. while (!StopEvent.WaitOne(TimeSpan.FromMilliseconds(sleepAmount))) { // Do you work here var foundIt = DidIFindIt(); if (foundIt) { // Signal all threads now to stop working we found it. StopEvent.Set(); } } } private static bool DidIFindIt() { return true; } } 

这是一本关于线程的优秀免费书籍。