Подтвердить что ты не робот

С# Thread не будет спать?

У меня есть этот код:

void Main()
{
    System.Timers.Timer t = new System.Timers.Timer (1000);
    t.Enabled=true;
    t.Elapsed+= (sender, args) =>c();
    Console.ReadLine();

}

int h=0;
public void c()
{
    h++;
    new Thread(() => doWork(h)).Start();
}

public void doWork(int h)
{
    Thread.Sleep(3000);
    h.Dump();
}

Я хотел посмотреть, что произойдет, если интервал составляет 1000 мс, а процесс задания - 3000 мс.

Однако я увидел странное поведение - задержка 3000 мс происходит только при запуске!

Как я могу сделать каждый doWork sleep 3000 мс?

Как вы можете видеть здесь, сначала начинается 3-секундная задержка, а затем выполняется итерация по 1 секунде.

enter image description here

4b9b3361

Ответ 1

Каждый раз, когда таймер гаснет, вы начинаете поток, чтобы немного спать; эта нить полностью изолирована, и таймер будет продолжать стрелять каждую секунду. Фактически, таймер срабатывает каждую секунду , даже если вы перемещаете Sleep(3000) в c().

В настоящее время у вас есть:

1000 tick (start thread A)
2000 tick (start thread B)
3000 tick (start thread C)
4000 tick (start thread D, A prints line)
5000 tick (start thread E, B prints line)
6000 tick (start thread F, C prints line)
7000 tick (start thread G, D prints line)
8000 tick (start thread H, E prints line)
...

Непонятно, что вы пытаетесь сделать. Вы можете отключить таймер, когда вы не хотите, чтобы он срабатывал, и возобновить его снова, как только он готов, но неясно, какая цель Sleep() здесь. Другой вариант - это просто цикл while с Sleep() в нем. Простой и не требует много потоков.

Ответ 2

Каждую секунду вы начинаете новый поток с задержкой в ​​3 секунды. Это происходит следующим образом:

  • поток 1 старт
  • thread 2 start, thread 1 sleeps
  • thread 3 start, thread 2 sleeps, thread 1 sleeps
  • thread 4 start, thread 3 sleeps, thread 2 sleeps, thread 1 sleeps
  • thread 5 start, thread 4 sleeps, thread 3 sleeps, thread 2 sleeps, thread 1 dumps
  • thread 6 start, thread 5 sleeps, thread 4 sleeps, thread 3 sleeps, thread 2 dumps
  • thread 7 start, thread 6 sleeps, thread 5 sleeps, thread 4 sleeps, thread 3 dumps

Как вы можете видеть, каждый поток спит в течение 3 секунд, но свалка происходит каждую секунду.

Как работать с потоками? smth вот так:

void Main()
{
    new Thread(() => doWork()).Start();
    Console.ReadLine();
}

public void doWork()
{
    int h = 0;
    do
    {
        Thread.Sleep(3000);
        h.Dump();
        h++;
    }while(true);
}

Ответ 3

Ваш пример очень интересный - он показывает побочные эффекты параллельной обработки. Чтобы ответить на ваш вопрос и облегчить просмотр побочных эффектов, я немного изменил ваш пример:

void Main() 
{ 
    System.Timers.Timer t = new System.Timers.Timer (10); 
    t.Enabled=true; 
    t.Elapsed+= (sender, args) =>c(); 
    Console.ReadLine(); 
} 

int t=0; int h=0; 

public void c() 
{ 
    h++; new Thread(() => doWork(h)).Start(); 
} 
public void doWork(int h2) 
{ 
    try 
    {
        t++; string.Format("h={0}, h2={1}, threads={2} [start]", 
                            h, h2, t).Dump();
        Thread.Sleep(3000); 
    }
    finally {
        t--; string.Format("h={0}, h2={1}, threads={2} [end]", 
                            h, h2, t).Dump();
    }
} 

Я изменил здесь следующее:

  • Интервал таймера теперь составляет 10 мс, потоки имеют еще 3000 мс. Эффект заключается в том, что, пока нити будут спать, будут созданы новые потоки.
  • Я добавил varialbe t, который подсчитывает количество активных в настоящее время потоков (увеличивается, когда поток запускается и уменьшается прямо перед концом потока)
  • Я добавил 2 оператора дампа, распечатав начало потока и конец потока
  • Наконец, я дал параметру функции doWork другое имя (h2), которое позволяет увидеть значение базовой переменной h

Теперь интересно просмотреть выходные данные этой измененной программы в LinqPad (обратите внимание, что значения не всегда совпадают, так как они зависят от расы условия начатых потоков):

    h=1, h2=1, threads=1 [start]
    h=2, h2=2, threads=2 [start]
    h=3, h2=3, threads=3 [start]
    h=4, h2=4, threads=4 [start]
    h=5, h2=5, threads=5 [start]
    ...
    h=190, h2=190, threads=190 [start]
    h=191, h2=191, threads=191 [start]
    h=192, h2=192, threads=192 [start]
    h=193, h2=193, threads=193 [start]
    h=194, h2=194, threads=194 [start]
    h=194, h2=2, threads=192 [end]
    h=194, h2=1, threads=192 [end]
    h=194, h2=3, threads=191 [end]
    h=195, h2=195, threads=192 [start]

Я думаю, что ценности говорят сами за себя: происходит то, что каждые 10 мс запускается новый поток, а другие все еще спят. Также интересно видеть, что h не всегда равно h2, особенно если больше потоков не запущено, пока другие спят. Количество потоков (переменная t) после стабилизируется, т.е. Работает около 190-194.

Вы можете утверждать, что нам нужно поставить блокировки на переменные t и h, например

readonly object o1 = new object(); 
int _t=0; 
int t {
       get {int tmp=0; lock(o1) { tmp=_t; } return tmp; } 
       set {lock(o1) { _t=value; }} 
      }

Хотя это более чистый подход, он не изменил эффект, показанный в этом примере.

Теперь, чтобы доказать, что каждый поток действительно спит 3000ms (= 3s), добавьте Stopwatch к рабочему потоку doWork:

public void doWork(int h2) 
{ 
    Stopwatch sw = new Stopwatch(); sw.Start();
    try 
    {
        t++; string.Format("h={0}, h2={1}, threads={2} [start]", 
                            h, h2, t).Dump();                               
        Thread.Sleep(3000);         }
    finally {
        sw.Stop(); var tim = sw.Elapsed;
        var elapsedMS = tim.Seconds*1000+tim.Milliseconds;
        t--; string.Format("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", 
                            h, h2, t, elapsedMS).Dump();
    }
} 

Для правильной очистки потоков отключите таймер после ReadLine следующим образом:

    Console.ReadLine(); t.Enabled=false; 

Это позволяет вам увидеть, что произойдет, если больше не запускается нить, после того, как вы нажмете ENTER:

    ...
    h=563, h2=559, threads=5 [end, sleep time=3105 ms] 
    h=563, h2=561, threads=4 [end, sleep time=3073 ms] 
    h=563, h2=558, threads=3 [end, sleep time=3117 ms] 
    h=563, h2=560, threads=2 [end, sleep time=3085 ms] 
    h=563, h2=562, threads=1 [end, sleep time=3054 ms] 
    h=563, h2=563, threads=0 [end, sleep time=3053 ms] 

Вы можете видеть, что все они заканчиваются один за другим, как ожидалось, и они спали около 3 секунд (или 3000 мс).

Ответ 4

Причина, по которой вы видите это поведение, проста: вы планируете новый поток каждую секунду, и результат становится видимым через три секунды. Вы не видите ничего за первые четыре секунды; затем, поток, который был запущен три секунды назад свалками; другой поток будет спать в течение двух секунд к тому времени, а еще один - в течение одной секунды. Следующий второй поток # 2 сбрасывает; затем поток # 3, # 4 и т.д. - вы получаете распечатку каждую секунду.

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

Ответ 5

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

Ответ 6

Каждая doWork спадает в течение трех секунд, но их сон перекрывается, потому что вы создаете потоки с интервалом в одну секунду.