Ваша первая попытка довольно хорошая, но поток продолжал существовать даже после выхода из приложения, поскольку вы не установили для свойства IsBackground
значение true
... вот упрощенная (и улучшенная) версияВаш код:
MyObject myObject = this.MyObject;
Thread t = new Thread(()=>
{
Thread.Sleep(1000); // wait a second (for a specific reason)
DoTheCodeThatNeedsToRunAsynchronously();
myObject.ChangeSomeProperty();
});
t.IsBackground = true;
t.Start();
Что касается безопасности потоков: трудно сказать, правильно ли работает ваша программа, когда несколько потоков выполняются одновременно, потому что вы не демонстрируете нам какие-либо спорные моменты в своем примере. очень возможно, что у вас возникнут проблемы с параллелизмом, если ваша программа конфликтует по MyObject
.
В Java есть ключевое слово final
, а в C # - соответствующее ключевое слово readonly
, нони final
, ни readonly
не гарантируют, что состояние изменяемого объекта будет согласованным между потоками.Единственное, что делают эти ключевые слова, это гарантируют, что вы не измените ссылку, на которую указывает объект.Если два потока имеют конфликт чтения / записи для одного и того же объекта, то вам следует выполнить какой-либо тип синхронизации или элементарные операции с этим объектом для обеспечения безопасности потока.
Обновить
ОК, еслиВы изменяете ссылку, на которую указывает myObject
, тогда ваша конкуренция теперь составляет myObject
.Я уверен, что мой ответ не будет соответствовать вашей реальной ситуации на 100%, но, учитывая приведенный вами пример кода, я могу сказать вам, что произойдет:
Вы будете не будьте уверены, какой объект будет изменен: это может быть that.MyObject
или this.MyObject
.Это верно независимо от того, работаете ли вы с Java или C #.Планировщик может запланировать выполнение вашего потока / таймера до, после или во время второго назначения.Если вы рассчитываете на определенный порядок исполнения, вам нужно сделать что-то , чтобы обеспечить этот порядок исполнения.Обычно это что-то является связью между потоками в форме сигнала: ManualResetEvent
, Join
или что-то еще.
Вот пример соединения:
MyObject myObject = this.MyObject;
Thread task = new Thread(()=>
{
Thread.Sleep(1000); // wait a second (for a specific reason)
DoTheCodeThatNeedsToRunAsynchronously();
myObject.ChangeSomeProperty();
});
task.IsBackground = true;
task.Start();
task.Join(); // blocks the main thread until the task thread is finished
myObject = that.MyObject; // the assignment will happen after the task is complete
Вот пример ManualResetEvent
:
ManualResetEvent done = new ManualResetEvent(false);
MyObject myObject = this.MyObject;
Thread task = new Thread(()=>
{
Thread.Sleep(1000); // wait a second (for a specific reason)
DoTheCodeThatNeedsToRunAsynchronously();
myObject.ChangeSomeProperty();
done.Set();
});
task.IsBackground = true;
task.Start();
done.WaitOne(); // blocks the main thread until the task thread signals it's done
myObject = that.MyObject; // the assignment will happen after the task is done
Конечно, в этом случае бессмысленно даже создавать множественныепотоки, так как вы не позволите им работать одновременно.Один из способов избежать этого - не изменять ссылку на myObject
после запуска потока, тогда вам не нужно будет Join
или WaitOne
для ManualResetEvent
.
Итак, это приводит меня к вопросу: почему вы назначаете новый объект для myObject
?Является ли это частью цикла for, который запускает несколько потоков для выполнения нескольких асинхронных задач?