Как правильно сказать потоку, который выполняет цикл, чтобы выйти из цикла и сделать что-то еще?
Следующий код имеет недостаток, заключающийся в том, что рабочий поток не будет ни немедленно завершаться, ни выполнять окончательное действие после того, как основной поток сбросит дескриптор ожидания. Вместо этого он будет продолжать делать то, что делает, пока не достигнет следующей итерации цикла, после чего он будет заблокирован на неопределенный срок.
static void Main()
{
ManualResetEvent m = new ManualResetEvent(true); // or bool b = true
Thread thread = new Thread(new ThreadStart(delegate()
{
while(m.WaitOne()) //or while(b)
{
//do something
}
//perform final operation and exit
}));
thread.Start();
//do something
m.Reset(); //or b = false
//do something else
}
Следующий код имеет тот недостаток, что он использует метод Abort() (есть люди, которые говорят, что его следует избегать любой ценой), но он выполняет именно то, что я ищу: вынудить рабочий поток выйти из цикла как как только главный поток скажет это сделать, выполните последнюю операцию и завершите работу.
static void Main()
{
Thread thread = new Thread(new ThreadStart(delegate()
{
try
{
while(true)
{
//do something
}
}
catch(ThreadAbortException e)
{
//perform final operation and exit
}
}));
thread.Start();
//do something
thread.Abort();
//do something else
}
Поскольку ни одно из решений не является идеальным, какой правильный способ реализовать нужную мне функциональность?
(Я бы предпочел решение, не включающее задачи.net 4.5)
2 ответа
Вы можете использовать BackgroundWorker
static void Main()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
// do something
worker.CancelAsync();
// do something else
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(!worker.CancellationPending)
{
// do something
}
// perform final action
}
(Код не проверен)
Если вы не можете использовать.NET 4.5 (как я уже упоминал в комментарии), вы можете использовать логическое значение, чтобы отменить цикл. Здесь я изменил ваш второй вариант:
static void Main()
{
volatile bool keepGoing = true;
Thread thread = new Thread(new ThreadStart(delegate()
{
while(keepGoing)
{
//do something
}
//logic to perform when the thread is cancelled
}));
thread.Start();
//do something
keepGoing = false;
//do something else
}
Отметив значение bool как volatile, вы всегда будете иметь правильное значение при проверке. Этот подход также гарантирует, что любое действие, которое вы выполняли внутри цикла, завершено, а не оставлено в "грязном" состоянии.
http://msdn.microsoft.com/en-us/library/x13ttww7%28VS.80%29.aspx