简体   繁体   English

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

[英]How do I pause execution of a thread in a thread pool for a certain amount of time without blocking the thread entirely?

What I have is something similar to this: 我所拥有的是类似于此的东西:

//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;
}

The issue is that I need the result of that method returned to the method that called it, however, I do not want it returned immediately and I do not want to block the thread. 问题是我需要将该方法的结果返回给调用它的方法,但是,我不希望它立即返回,我不想阻止该线程。 Is there a way that I can make the thread pause at that line for a specified amount of time and release the thread back into the thread pool, and then after some timeout, one of the thread pool threads picks up where it left off and completes the function and returns? 有没有一种方法可以让线程在该行暂停一段指定的时间并将线程释放回线程池,然后在一些超时后,其中一个线程池线程从中断处完成并完成功能和回报?


All... After spending some time with your responses, I realize now that doing what I am trying to achieve is not so much possible in the context in which I want it. 所有......在花了一些时间回答你的问题之后,我现在意识到,在我想要的环境中,做我想要实现的目标并不是那么可能。

A little explanation though. 虽然有点解释。 I am trying to create a rudimentary long poll web server in c sharp for a web based chat application I am building. 我正在尝试用c sharp创建一个基本的长轮询网络服务器,用于我正在构建的基于Web的聊天应用程序。 I was wanting to have the thread enter a method where it waits until one of two things happens, either data shows up for the client, or a poll timeout occurs. 我想让线程进入一个等待的方法,直到发生两件事之一,或者为客户端显示数据,或者发生轮询超时。 However, I did not want the thread to block, I have some time in the past implemented something where each client got a thread and the thread blocked and trust me... it is NOT a good idea. 但是,我不希望线程被阻塞,我有一些时间过去实现了一些东西,每个客户端都有一个线程,线程被阻塞并相信我......这不是一个好主意。 The 5 seconds in the example was arbitrary and the actual time will likely sit somewhere between 1 and 5 minutes. 示例中的5秒是任意的,实际时间可能介于1到5分钟之间。 In the end, the structure I may end up going with is a sort of client management thread. 最后,我最终可能会遇到的结构是一种客户端管理线程。 a thread that works its way through a list asking each client if the client has work to be done. 一个线程,通过一个列表询问每个客户端客户端是否有工作要做。 If the client has reached its timeout or has data waiting in its queue, then the appropriate method gets dispatched into the thread pool and and the Client Management thread continues on to the next client. 如果客户端已达到其超时或其队列中有数据等待,则将相应的方法分派到线程池中,并且客户端管理线程继续到下一个客户端。

Something like so... 像这样......

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();
        }
    }
}

Thank you all considerably for your help and patience! 非常感谢大家的帮助和耐心!

After looking through your question, I see it's a bit trickier than just getting a timer callback on a new thread. 在查看了你的问题后,我发现它比在新线程上获得定时器回调有点棘手。 The real issue is that your call is structured to return a value synchronously, so there is no way you can avoid blocking the calling thread. 真正的问题是你的调用被构造为同步返回一个值,所以你无法避免阻塞调用线程。 The correct way to avoid blocking is to change your call to use an asynchronous callback rather than returning the value directly. 避免阻塞的正确方法是更改​​调用以使用异步回调,而不是直接返回值。 If you have access to the TPL, one nice way to do this is to return a Task object that the caller can then Wait on immediately or register for a callback. 如果您有权访问TPL,一个很好的方法是返回一个Task对象,然后调用者可以立即等待或注册回调。


That said, for the timing callback aspect, I have some utility code that I use to simplify working with System.Threading.Timer in these one-off timer scenarios. 也就是说,对于时序回调方面,我有一些实用程序代码,我用它来简化在这些一次性计时器方案中使用System.Threading.Timer。 I've cut it down to the specified functionality and put it below: 我已将其剪切为指定的功能并将其放在下面:

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; }
    }
}

using System.Threading.Timer Class. 使用System.Threading.Timer类。 Refer to the above MSDN link. 请参阅上面的MSDN链接。

The accepted answer was what I needed also, though after looking at it a little I think we can reduce it down to just this. 接受的答案也是我所需要的,虽然看了一下之后我认为我们可以将它降低到这一点。 And it seemed appropriate to name it after that JScript functions we all know and love... 在我们都知道并喜欢的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();
}

To really not block, stay on the same thread, and wait 5 seconds... 要真的不阻止,保持相同的线程,等待5秒......

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

There goes one of your CPUs for 5 seconds! 有一个CPU持续5秒! I really hope you don't have a single core available to the process. 我真的希望你没有一个核心可用于这个过程。

Still, that's what it means to have a thread wait for 5 seconds without blocking. 不过,这就是让线程在没有阻塞的情况下等待5秒的意思。 A thread that isn't waiting on something is using CPU time. 没有等待某事的线程正在使用CPU时间。 While we strive to have our CPUs busy as long as there is work for them to be doing, we strive to have them to be busy with real work. 虽然我们努力让我们的CPU忙碌,只要他们有工作要做,我们就会努力让他们忙于实际工作。

Sometimes code similar to the above is used for very small pauses on the order of nanoseconds, to avoid context switches when the condition the thread wants to see before progressing is likely to happen soon. 有时类似于上面的代码用于纳秒级的非常小的暂停,以避免在线程想要在进展之前看到的条件很快发生时的上下文切换。

This is of no value here, since a thread's time slice is typically in the range of 20ms to 180ms, it's clearly going to be up within the 5second period. 这在这里没有价值,因为线程的时间片通常在20ms到180ms的范围内,它显然会在5秒的时间内上升。

While we often want to avoid threads blocking, and may go to considerable lengths to avoid it, in this case if we really have to stay on the same thread, then we're a waste of CPU and are going to context-switch anyway, so really the sooner we block the better. 虽然我们经常想避免线程阻塞,并且可能会花费相当长的时间来避免它,但在这种情况下,如果我们真的必须保持在同一个线程上,那么我们就是浪费CPU并且无论如何都要进行上下文切换,所以我们越早阻止越好。 The simplest way of doing this would be Thread.Sleep(5000) . 最简单的方法是Thread.Sleep(5000)

Alternatively, having failed to deliver one of the three requirements (don't block, stay on the same thread and return to the method synchronously, cause a 5 second delay) by blocking, we can change to delivering another two. 或者,由于未能通过阻塞来交付三个要求中的一个(不阻塞,保持在同一个线程并同步返回到方法,导致5秒延迟),我们可以改为另外两个。

If we use a call-back then we won't be returning the value to the calling thread, but we will prevent threads from blocking unnecessarily, and this is probably the best over-all approach. 如果我们使用回调,那么我们不会将值返回给调用线程,但是我们将阻止线程不必要地阻塞,这可能是最好的整体方法。 This will require more restructuring though. 这将需要更多的重组。

Of course, if we can work out a way to avoid the other of the three requirement, that we wait 5 seconds at all, then all the other problems go away and we're 5 seconds faster! 当然,如果我们能够找到一种方法来避免三个要求中的另一个,我们等待5秒钟,那么所有其他问题就会消失,我们会快5秒!

Both of these last two approaches are better than anything involving Thread.Sleep (98% of code that calls that is something that can be improved somewhere, and close to 100% of code that calls it with a value higher than around 2 is). 这两种方法都比涉及Thread.Sleep任何方法都好(98%的代码调用可以在某处改进的东西,接近100%的代码调用它的值大于2左右)。 It's hard to give clear advice on just how to do this without more context as to why you need to wait 5seconds in the first place though. 如果没有更多关于为什么你需要在第一时间等待5秒的情况下,如何做到这一点很难给出明确的建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM