线程正忙等待
本文关键字:忙等待 线程 | 更新日期: 2023-09-27 18:27:15
基本上,我需要忙着等待网页上出现一些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
,它们都应该挂起。
关于这种方法,我有几个问题。首先,这是一个很好的方法。其次,每个线程都会得到自己的繁忙等待函数的"副本"吗。我的意思是,一个线程是否可以抢占另一个线程并更改该函数中的数据,或者他们每个线程是否都获得函数中声明的变量的副本。请注意,这两个函数都在同一个对象内。
在回答您的问题之前,我必须指出您已经做出了关闭循环变量的惊人行为。
首先,这是一个很好的方法。
不,不是真的。任意创建线程通常不是一个好主意。最好使用线程池技术。这可以通过ThreadPool.QueueUserWorkItem
或Task
类来完成。
其次,每个线程都会得到自己的繁忙等待函数的"副本"吗。我的意思是,一个线程是否可以抢占另一个线程并更改中的数据该函数,或者他们每个人都得到声明的变量的副本函数内部。
BusyWait
的每个运行实例都将获得其自己的所有局部变量(即page
和request
)的副本。由于found
是在非本地作用域中声明的(可能无论如何),因此它将在BusyWait
的所有运行实例之间共享。因此,当前对found
的读取和写入不是线程安全的,因为没有适当的同步机制。
第二,每个线程会得到自己的繁忙等待函数的"副本"吗
每个线程都将使用自己的堆栈空间执行函数,这意味着函数内部的任何本地变量都将属于正在运行的线程。如果你有一个全局变量,比如函数内部更改的found
变量,你需要设置一个同步机制,这样多个线程就不会同时访问它,因为这将导致很难找到的错误和许多你从未想过要经历的恐怖!
所有线程都有自己的局部变量副本(在这种情况下只有string page
)。
您共享的found
变量应声明为volatile
这是一种罕见的情况,调用Thread.Sleep()
可能会带来一些好处。在对同一个站点的调用之间插入一个小通气口。
每个线程都使用自己的变量副本运行。
然而,我会很快修改我的道歉。使用找到的变量不是线程安全的。一次可能会发现多个线程正在更改。也有可能一个线程正在读取它,而另一个线程在写入它。[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<object> 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<object> 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;
}
}
这是一本优秀的免费线程书籍。