Выполняются ли таймеры С# в отдельном потоке?
Выполняется ли в System.Timers.Timer отдельный поток, чем поток, который его создал?
Допустим, у меня есть класс с таймером, который срабатывает каждые 5 секунд. Когда таймер срабатывает, в прошедшем методе некоторый объект изменяется. Допустим, что для изменения этого объекта требуется много времени, например 10 секунд. Возможно ли, что я столкнулся с конфликтами потоков в этом сценарии?
Ответы
Ответ 1
Для System.Timers.Timer:
Смотрите ответ Брайана Гидеона ниже
Для System.Threading.Timer:
Документация MSDN по таймерам:
Класс System.Threading.Timer делает обратные вызовы в потоке ThreadPool и вообще не использует модель события.
Таким образом, таймер истекает в другом потоке.
Ответ 2
Это зависит. System.Timers.Timer
имеет два режима работы.
Если SynchronizingObject
установлен в экземпляр ISynchronizeInvoke
, то событие Elapsed
будет выполняться в потоке, на котором размещается объект синхронизации. Обычно эти экземпляры ISynchronizeInvoke
представляют собой не что иное, как простые старые Control
и Form
экземпляры, с которыми мы все знакомы. Поэтому в этом случае событие Elapsed
вызывается в потоке пользовательского интерфейса и ведет себя аналогично System.Windows.Forms.Timer
. В противном случае это действительно зависит от конкретного экземпляра ISynchronizeInvoke
, который был использован.
Если SynchronizingObject
равно null, то событие Elapsed
вызывается в потоке ThreadPool
и ведет себя аналогично System.Threading.Timer
. Фактически, он фактически использует System.Threading.Timer
за кулисами и выполняет операцию маршалинга после получения обратного вызова таймера, если это необходимо.
Ответ 3
Каждое прошедшее событие будет срабатывать в том же потоке, если только предыдущий Истекший не запущен.
Таким образом, он обрабатывает столкновение для вас
попробуйте поместить его в консоль
static void Main(string[] args)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
var timer = new Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start();
Console.ReadLine();
}
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Thread.Sleep(2000);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
вы получите что-то вроде этого
10
6
12
6
12
где 10 - вызывающий поток, а 6 и 12 стреляют из прошедшего bg события.
Если вы удалите Thread.Sleep(2000); вы получите что-то вроде этого
10
6
6
6
6
Так как никаких столкновений нет.
Но это все еще оставляет проблему с проблемой. если u запускает событие каждые 5 секунд, и для редактирования требуется 10 секунд, вам нужно заблокировать некоторые пропуски.
Ответ 4
Если прошедшее событие занимает больше времени, то интервал, он создаст еще один поток, чтобы поднять прошедшее событие. Но есть обходной путь для этого
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
timer.Stop();
Thread.Sleep(2000);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
finally
{
timer.Start();
}
}
Ответ 5
Для System.Timers.Timer, в отдельном потоке, если SynchronizingObject не установлен.
static System.Timers.Timer DummyTimer = null;
static void Main(string[] args)
{
try
{
Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval
DummyTimer.Enabled = true;
DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired);
DummyTimer.AutoReset = true;
DummyTimer.Start();
Console.WriteLine("Hit any key to exit");
Console.ReadLine();
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
return;
}
static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
return;
}
Вы увидите, будет ли DummyTimer запущен с интервалом 5 секунд:
Main Thread Id: 9
12
12
12
12
12
...
Итак, как видно, OnDummyTimerFired выполняется в потоке Workers.
Нет, дальнейшее усложнение. Если вы сократите интервал до 10 мс,
Main Thread Id: 9
11
13
12
22
17
...
Это связано с тем, что если предыдущее исполнение OnDummyTimerFired не выполняется при следующем запуске следующего тика, тогда .NET создаст новый поток для выполнения этой задачи.
Далее усложняется: "Класс System.Timers.Timer предоставляет простой способ справиться с этой дилеммой - он предоставляет общедоступное свойство SynchronizingObject. Установка этого свойства в экземпляр Windows Form (или элемент управления в Windows Form), убедитесь, что код в обработчике событий Elapsed работает в том же потоке, на котором был создан экземпляр SynchronizingObject.
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx#S2