Поток C# не будет спать?
у меня есть этот код :
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
сон 3000 МС?
Как вы можете видеть здесь, в начале есть 3-секундная задержка, а затем он повторяет 1 секунду каждый.
6 ответов:
каждый раз, когда таймер тикает, вы начинаете поток, чтобы сделать некоторый сон; этот поток полностью изолирован, и таймер будет продолжать стрелять каждую секунду. На самом деле, таймер срабатывает каждую секунду даже если перемещение
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()
в нем. Просто, и не включает много потоков.
каждую секунду вы начинаете новый поток с задержкой 3 сек. Это происходит так:
- поток 1 старт
- запустить поток 2, поток 1 проживающих
- поток 3 пуск, поток 2 спит, поток 1 спит
- резьба 4 пуск, резьба 3 спальных мест, 2 спальных места резьбы, резьба 1 проживающих
- резьба 5 пуск, резьба 4 спальных места, 3 спальных места резьбы, резьба 2 спальных места, 1 нить дампы
- резьба 6 начало, нить, 5 спальных мест, 4 спальных места резьбы, резьба 3 спальных мест, 2-нить сваливает
- нить 7 Пуск, резьба 6 проживающих, резьба 5 спальных мест, 4 спальных места резьбы, резьба 3 свалки
Как вы можете видеть, каждый поток спит в течение 3 секунд, но дамп происходит каждую секунду.
Как работать с потоками? что-то вроде этого:
void Main() { new Thread(() => doWork()).Start(); Console.ReadLine(); } public void doWork() { int h = 0; do { Thread.Sleep(3000); h.Dump(); h++; }while(true); }
ваш пример очень интересен-он показывает побочные эффекты параллельной обработки. Чтобы ответить на ваш вопрос и облегчить просмотр побочных эффектов, я немного изменил ваш пример:
using System; using System.Threading; using System.Diagnostics; public class Program { public static void Main() { (new Example()).Main(); } } public class Example { public void Main() { System.Timers.Timer t = new System.Timers.Timer(10); t.Enabled = true; t.Elapsed += (sender, args) => c(); Console.ReadLine(); t.Enabled = false; } int t = 0; int h = 0; public void c() { h++; new Thread(() => doWork(h)).Start(); } public void doWork(int h2) { Stopwatch sw = new Stopwatch(); sw.Start(); try { t++; Console.WriteLine("h={0}, h2={1}, threads={2} [start]", h, h2, t); Thread.Sleep(3000); } finally { sw.Stop(); var tim = sw.Elapsed; var elapsedMS = tim.Seconds * 1000 + tim.Milliseconds; t--; Console.WriteLine("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", h, h2, t, elapsedMS); } } }
то, что я изменил здесь следующее:
- интервальный таймер теперь 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]
вы можете видеть, что они все прекращаются один за другим, как ожидалось, и они спали около 3s (или 3000ms).
причина, по которой вы видите это поведение, проста: вы планируете новый поток каждую секунду, а результат становится видимым через три секунды. Вы ничего не видите в течение первых четырех секунд; затем поток, который был запущен три секунды назад, сбрасывается; другой поток будет спать в течение двух секунд к тому времени, а еще один - в течение одной секунды. Следующий второй поток #2 сбрасывает; затем поток #3, #4 и так далее - вы получаете распечатку каждую секунду.
Если вы хотите смотрите распечатку каждые три секунды, вы должны планировать новый поток каждые три секунды с любой задержкой, которую вы хотели бы: начальный поток будет выводиться через три секунды плюс задержка; все последующие потоки будут срабатывать с трехсекундными интервалами.