C# 忙线程等待

C# 忙线程等待,c#,.net,multithreading,thread-safety,C#,.net,Multithreading,Thread Safety,基本上,我需要等待一些html出现在网页上。我已创建了以下代码以等待我: public void ExecuteBusyWaitThreads() { foreach (Canidate canidate in allCanidates) { Thread newThread = new Thread(delegate() { BusyWait(canidate);

基本上,我需要等待一些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
,它们都应该退出

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

第二,每个线程是否会获得自己的忙等待函数的“副本”


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

所有线程都会获得自己的局部变量副本(在本例中,仅
字符串页

找到的共享
变量应声明为volatile


这是一种罕见的情况,调用
Thread.Sleep()
可能会有一些好处。在对同一站点的调用之间插入一个小呼吸器

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

然而,我会修改我的回答。使用找到的变量不是线程安全的。一次可能会发现多个线程在更改。也很可能一个线程正在读它,而另一个线程正在写它。
[lock][1]
可以避免这种情况

解决此问题的更好方法是使用。这样,您就不必担心锁定问题,您可以在睡眠或超时状态下构建线程,因此,如果没有出现“全部声明”,您的线程将不会比您希望的运行时间长

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;
    }
}
内部类示例执行
{
私有静态事件WaitHandle\u stopEvent;
公共静态事件WaitHandle StopEvent
{
获取{return _stopEvent???(_stopEvent=new EventWaitHandle(false,EventResetMode.ManualReset));}
}
公共静态void派生线程(IEnumerable someCollection)
{
foreach(someCollection中的变量项)
{
//您可能不希望手动创建线程,因为理想情况下,这些线程将是小型工人
//而动作BeingInvoke在线程池中运行
行动过程=忙碌等待;
process.BeginInvoke(item,null,null);
}
}
私有静态void BusyWait(对象obj)
{
//您可以等待任意长的时间,或者0根本不等待
常数int sleepAmount=1;
//阻塞当前线程,直到当前实例接收到信号,使用
//用于指定时间间隔的System.TimeSpan。
//
//参数:
//超时:
//System.TimeSpan,表示等待的毫秒数,或
//System.TimeSpan,表示无限期等待的时间为-1毫秒。
//
//返回:
//如果当前实例接收到信号,则为true;否则为false。
而(!StopEvent.WaitOne(TimeSpan.FromMillicles(sleepAmount)))
{
//你在这里工作吗
var foundIt=DidIFindIt();
如果(找到它)
{
//现在通知所有线程停止工作,我们找到了它。
StopEvent.Set();
}
}
}
私有静态bool DidIFindIt()
{
返回true;
}
}

是一本优秀的免费书籍。

在我回答你的问题之前,我必须指出你犯下了令人发指的罪行

首先,这是一个好方法

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

其次,每个线程都将获得自己的忙等待函数的“副本”。 我的意思是,一个线程可以抢占另一个线程并更改其中的数据吗 那个函数,或者他们每个人都得到一个声明变量的副本 函数内部


BusyWait
的每个运行实例将获得所有局部变量(即
page
request
)的副本。由于
found
是在非本地范围内声明的(可能无论如何),因此它将在
BusyWait
的所有运行实例之间共享。因此,您当前对
的读写操作发现
不是线程安全的,因为没有同步机制。

这是一个有效的技术问题,但看起来像是在扑克或拍卖中作弊。我确实想过要这么做。但是,您能否详细说明添加Thread.Sleep()的好处。我的申请实际上是在和别人竞争。首先检测“全部索赔”按钮非常重要。添加Thread.Sleep()会有助于还是有害于这种情况?ThanksA short Sleep()将减轻系统的其他负担,使处理8项任务更加平等。但要想在最后一个毫秒前保持竞争力,请不要睡觉()。谢谢你的回答。你可能已经解决了我看到的另一个bug。这样我才能正确理解这篇文章。在我的foreach循环中,我需要创建一个保存canidate的新变量,然后将其传递给BusyWait函数?很好,我完全错过了它。(修订)