Как приостановить выполнение потока в пуле потоков на определенное время, не блокируя поток полностью? - PullRequest
3 голосов
/ 20 января 2012

У меня есть что-то похожее на это:

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

Проблема в том, что мне нужно, чтобы результат этого метода возвращался методу, который его вызвал, однако я не хочу, чтобы он возвращался немедленно иЯ не хочу, чтобы заблокировать поток.Есть ли способ, которым я могу сделать приостановку потока в этой строке на определенное время и выпустить поток обратно в пул потоков, а затем через некоторое время один из потоков пула потоков обнаружит, где он остановился, и завершит работу?функция и возвращает?


Все ... Потратив некоторое время с вашими ответами, я теперь понимаю, что делать то, что я пытаюсь достичь, не так много возможно в контексте, в котором я хочу это,

Небольшое объяснение.Я пытаюсь создать элементарный длинный опросный веб-сервер в тесном контакте с веб-приложением чата, которое я создаю.Я хотел, чтобы поток ввел метод, в котором он ожидает, пока не произойдет одно из двух: либо данные будут показаны для клиента, либо произойдет тайм-аут опроса.Однако я не хотел, чтобы поток блокировался, я когда-то в прошлом реализовывал что-то, где каждый клиент получал поток, а поток блокировал и доверял мне ... это НЕ хорошая идея.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 ]

3 голосов
/ 20 января 2012

Изучив ваш вопрос, я вижу, что это немного сложнее, чем просто получить обратный вызов таймера в новом потоке.Реальная проблема заключается в том, что ваш вызов структурирован так, чтобы возвращать значение синхронно, поэтому нет способа избежать блокирования вызывающего потока.Правильный способ избежать блокировки - изменить свой вызов на использование асинхронного обратного вызова, а не возвращать значение напрямую.Если у вас есть доступ к 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 голос
/ 20 января 2012

с использованием System.Threading.Timer Class.См. Ссылку MSDN выше.

0 голосов
/ 28 ноября 2013

Был принят и тот ответ, который мне был нужен, хотя, посмотрев немного, я думаю, что мы можем свести его к этому. И казалось уместным назвать его после того 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();
}
0 голосов
/ 20 января 2012

Чтобы действительно не блокировать, оставайтесь в том же потоке и ждите 5 секунд ...

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

Один из ваших процессоров работает 5 секунд! Я действительно надеюсь, что у вас нет ни одного ядра, доступного для процесса.

Тем не менее, это означает, что поток должен ждать 5 секунд без блокировки. Поток, который не ожидает чего-то, использует процессорное время. Хотя мы стремимся к тому, чтобы наши процессоры были заняты, пока у них есть работа, мы стремимся к тому, чтобы они были заняты реальной работой.

Иногда код, подобный приведенному выше, используется для очень маленьких пауз порядка наносекунд, чтобы избежать переключений контекста, когда условие, которое поток хочет видеть перед выполнением, может произойти в ближайшее время.

Здесь это не имеет значения, поскольку временной интервал потока обычно находится в диапазоне от 20 мс до 180 мс, он явно будет расти в течение 5-секундного периода.

Несмотря на то, что мы часто хотим избежать блокировки потоков и можем пойти на все, чтобы избежать этого, в этом случае, если нам действительно придется остаться в одном потоке, мы тратим ЦП и собираемся в контекст- в любом случае переключайтесь, так что чем раньше мы заблокируем, тем лучше. Самый простой способ сделать это будет Thread.Sleep(5000).

В качестве альтернативы, если не удалось выполнить одно из трех требований (не блокировать, остаться в том же потоке и вернуться к методу синхронно, вызвать 5-секундную задержку) путем блокировки, мы можем перейти к доставке еще двух.

Если мы используем обратный вызов, то мы не будем возвращать значение вызывающему потоку, но будем предотвращать ненужную блокировку потоков, и это, вероятно, лучший общий подход. Это потребует дополнительной реструктуризации.

Конечно, если мы сможем выработать способ избежать другого из трех требований, что мы вообще будем ждать 5 секунд, тогда все остальные проблемы исчезнут, и мы на 5 секунд быстрее!

Оба этих последних двух подхода лучше, чем что-либо, включающее Thread.Sleep (98% кода, который вызывает что-то, что может быть улучшено где-то, и близко к 100% кода, который вызывает его со значением выше, чем около 2 является). Трудно дать четкий совет о том, как это сделать без дополнительного контекста, почему вам нужно ждать 5 секунд в первую очередь.

...