如何暂停线程池中线程的执行一段时间而不完全阻塞线程?

时间:2012-01-19 23:11:26

标签: c# .net multithreading asynchronous threadpool

我所拥有的是类似的东西:

//method being called by thread pool thread
public string someFunction(){
    string someString = "string";

    //Stuff happens

    //Need to wait for 5 seconds without blocking thread

    return someString;
}

问题是我需要将该方法的结果返回给调用它的方法,但是,我不希望它立即返回,我不想阻止该线程。有没有一种方法可以让线程在该行暂停一段指定的时间并将线程释放回线程池,然后在一些超时后,其中一个线程池线程从中断处完成并完成功能和退货?


所有......在花了一些时间回答你的问题之后,我现在意识到,在我想要的环境中,做我想要实现的目标并非如此。

虽然有点解释。我正在尝试用c sharp创建一个基本的长轮询网络服务器,用于我正在构建的基于Web的聊天应用程序。我想让线程进入一个等待的方法,直到发生两件事之一,或者为客户端显示数据,或者发生轮询超时。但是,我不希望线程被阻塞,我有一些时间过去实现了一些东西,每个客户端都有一个线程,线程被阻塞并相信我......这不是一个好主意。示例中的5秒是任意的,实际时间可能介于1到5分钟之间。最后,我最终可能会遇到的结构是一种客户端管理线程。一个线程,通过一个列表询问每个客户端客户端是否有工作要做。如果客户端已达到其超时或队列中有数据等待,则相应的方法将被分派到线程池中,并且客户端管理线程将继续到下一个客户端。

像这样......

while(true){
    foreach(Client client in ClientList){
        //check if the client has something it needs done
        if(client.needsWork){
               //invoke the appropriate method asynchronously
               delegate = client.appropriateInvokable;
               delegate.beginInvoke();
        }
    }
}

非常感谢你的帮助和耐心!

4 个答案:

答案 0 :(得分:3)

在查看了你的问题后,我发现它比在新线程上获得定时器回调有点棘手。真正的问题是你的调用被构造为同步返回一个值,所以你无法避免阻塞调用线程。避免阻塞的正确方法是更改​​调用以使用异步回调,而不是直接返回值。如果您有权访问TPL,一个很好的方法是返回一个Task对象,然后调用者可以立即等待或注册回调。


也就是说,对于时序回调方面,我使用了一些实用程序代码来简化在这些一次性计时器方案中使用System.Threading.Timer的过程。我已将其降低到指定的功能并将其放在下面:

public sealed class TimerService
{
    /// <summary>
    ///   This method registers a call back to be called after a specified period of time.
    /// </summary>
    /// <param name = "duration">The duration after which to call back</param>
    /// <param name = "callback">The method to call back</param>
    public void WhenElapsed(TimeSpan duration, Action callback)
    {
        if (callback == null) throw new ArgumentNullException("callback");

        //Set up state to allow cleanup after timer completes
        var timerState = new TimerState(callback);
        var timer = new Timer(OnTimerElapsed, timerState, Timeout.Infinite, Timeout.Infinite);
        timerState.Timer = timer;

        //Start the timer
        timer.Change((int)duration.TotalMilliseconds, Timeout.Infinite);
    }

    private void OnTimerElapsed(Object state)
    {
        var timerState = (TimerState)state;
        timerState.Timer.Dispose();
        timerState.Callback();
    }

    private sealed class TimerState
    {
        public TimerState(Action callback)
        {
            Callback = callback;
        }

        public Timer Timer { get; set; }

        public Action Callback { get; private set; }
    }
}

答案 1 :(得分:1)

使用System.Threading.Timer类。请参阅上面的MSDN链接。

答案 2 :(得分:0)

要真的不阻止,保持同一个线程,等待5秒......

var until = Environment.TickCount + 5000;
while(Environment.TickCount < until);

你的一个CPU持续5秒!我真的希望你没有一个可用的核心。

但是,这就是让线程在没有阻塞的情况下等待5秒的意思。没有等待某事的线程正在使用CPU时间。虽然我们努力让我们的CPU忙碌,只要他们有工作要做,我们就会努力让他们忙于实际工作。

有时类似于上面的代码用于纳秒级的非常小的暂停,以避免在线程想要在进展之前看到的条件很快发生时的上下文切换。

这里没有任何价值,因为线程的时间片通常在20ms到180ms的范围内,它显然会在5秒的时间内上升。

虽然我们经常希望避免线程阻塞,并且可能会花费相当长的时间来避免它,但在这种情况下,如果我们真的必须保持在同一个线程上,那么我们就是浪费CPU而且会转向上下文 - 无论如何切换,所以我们越早阻止越好。最简单的方法是Thread.Sleep(5000)

或者,由于未能通过阻止提供三个要求中的一个(不阻塞,保持在同一个线程并同步返回到方法,导致5秒延迟),我们可以更改为另外两个。< / p>

如果我们使用回调,那么我们不会将值返回给调用线程,但是我们将阻止线程不必要地阻塞,这可能是最好的整体方法。这将需要更多的重组。

当然,如果我们能够找到一种方法来避免三个要求中的另一个,我们等待5秒钟,那么所有其他问题就会消失,我们会快5秒!

这两种方法都比涉及Thread.Sleep的任何方法都好(98%的代码调用可以在某处改进的代码,接近100%的代码调用它的值高于{1} 2左右)。如果没有更多的背景知识,为什么你需要在第一时间等待5秒,很难给出明确的建议。

答案 3 :(得分:0)

接受的答案也是我所需要的,虽然看了一下后我觉得我们可以将它降低到这一点。在我们都知道并喜欢的JScript函数之后命名它似乎是合适的......

public static void SetTimeout(Action callback, int milliseconds)
{
    Timer t = null;
    t = new Timer((state) => { t.Dispose(); callback(); }, null, dueTime: milliseconds, period: Timeout.Infinite);
}
public static Timer SetInterval(Action callback, int milliseconds)
{
    return new Timer((state) => { callback(); }, null, dueTime: milliseconds, period: milliseconds);
}
public static void ClearInterval(Timer t)
{
    t.Dispose();
}