Это, вероятно, один из самых частых вопросов в Stackoverflow, однако я не смог найти точный ответ на мой вопрос: Я хотел бы создать шаблон, который позволяет начать поток B из потока A и при определенных условиях (например, когда возникает исключение) вызвать метод в потоке A. В случае исключения правильная нить имеет большое значение, поскольку исключение должно вызовите метод catch в основном потоке A. Если поток A является потоком пользовательского интерфейса, тогда все просто (вызовите .Invoke()
или .BeginInvoke()
и все). В потоке пользовательского интерфейса есть некоторый механизм, как это делается, и я хотел бы получить представление о том, как можно было бы написать собственный механизм для потока, отличного от UI. Обычно предлагаемым способом для этого является использование перекачки сообщений
http://www.codeproject.com/Articles/32113/Understanding-SynchronizationContext-Part-II
, но цикл while
будет блокировать поток A, и это не то, что мне нужно, а не то, как поток потоков пользовательского интерфейса обрабатывает эту проблему. Существует несколько способов обойти эту проблему, но я хотел бы получить более глубокое понимание проблемы и написать свою собственную универсальную утилиту независимо от выбранных методов, например, используя System.Threading.Thread
или System.Threading.Tasks.Task
или BackgroundWorker
или что-нибудь еще и независимо, если есть поток пользовательского интерфейса или нет (например, консольное приложение).
Ниже приведен пример кода, который я пытаюсь использовать для проверки улавливания исключения (который четко указывает на неправильный поток, к которому выбрано исключение). Я буду использовать его как утилиту со всеми функциями блокировки, проверкой работы потока и т. Д., Поэтому я создаю экземпляр класса.метод вызова из другого потока без блокировки потока (или написать собственный SynchronizationContext для потока, отличного от UI). C#
class Program
{
static void Main(string[] args)
{
CustomThreads t = new CustomThreads();
try
{
// finally is called after the first action
t.RunCustomTask(ForceException, ThrowException); // Runs the ForceException and in a catch calls the ThrowException
// finally is never reached due to the unhandled Exception
t.RunCustomThread(ForceException, ThrowException);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// well, this is a lie but it is just an indication that thread B was called
Console.WriteLine("DONE, press any key");
Console.ReadKey();
}
private static void ThrowException(Exception ex)
{
throw new Exception(ex.Message, ex);
}
static void ForceException()
{
throw new Exception("Exception thrown");
}
}
public class CustomThreads
{
public void RunCustomTask(Action action, Action<Exception> action_on_exception)
{
Task.Factory.StartNew(() => PerformAction(action, action_on_exception));
}
public void RunCustomThread(Action action, Action<Exception> action_on_exception)
{
new Thread(() => PerformAction(action, action_on_exception)).Start();
}
private void PerformAction(Action action, Action<Exception> action_on_exception)
{
try
{
action();
}
catch (Exception ex)
{
action_on_exception.Invoke(ex);
}
finally
{
Console.WriteLine("Finally is called");
}
}
}
Еще одна интересная особенность, которую я обнаружил, что new Thread()
бросает необработанное исключение и finally
никогда не вызывается, тогда как new Task()
не делает, и finally
называется. Может быть, кто-то может прокомментировать причину этой разницы.
Почему бы просто не использовать 'async' и' await' вместо того, чтобы использовать некоторый код pre-TPL с датой 2008 года? Вы понимаете, что 'Invoke' и' BeginInvoke' используют насос сообщений за кулисами? – MickyD
выдержка из https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx 'В рамках обработанного исключения гарантированный запуск завершенного блока. Однако, если исключение необработанно, выполнение блока finally зависит от того, как инициируется операция отмены развязки' – mariovalens
«цикл while блокирует поток A и ... не способ, которым поток нитей обрабатывает эту проблему» - да , да. За кулисами поток пользовательского интерфейса работает в цикле while, так что он может подобрать работу, чтобы сделать - либо события пользовательского интерфейса из окон, либо запросы на запуск кода из других потоков. –