С# 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]()
Ответы
Ответ 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 спадает в течение трех секунд, но их сон перекрывается, потому что вы создаете потоки с интервалом в одну секунду.