c# поймать исключение из Delegate.begininvoke без вызова Delegate.Endinvoke - PullRequest
1 голос
/ 20 января 2020

У меня есть программа, которая контролирует БД (или несколько БД). для этого я создал класс, который содержит всю информацию о том, как контролировать БД. класс содержит делегата, который указывает на функцию, которая контролирует БД и соответственно изменяет поле состояния.

основной поток создает новый экземпляр класса и вызывает class.delegate.begininvoke (). основной поток проверяет состояние каждого созданного класса в al oop и информирует пользователя о любых изменениях.

простой пример кода:

Class Monitor
{
    private Object lockObj;
    private delegate void MonitorHandlerDelegate();
    private MonitorHandlerDelegate mainHandler;

    private int State;
    private int DBid;

    public Monitor(int DBid)
    {
        this.DBid = DBid;
        mainHandler += MonitorHandler;
        lockObj = new Object();
    }

    private void MonitorHandler()
    {
        do
        {
            state = CheckDB(DBid); // 0 is ok, 1 is fail, 2 is InWork, 3 is stop monitoring
        } while (state != 3);
    }

    public int state
    {
        get { lock(lockObj) { return State;} }
        set { lock(lockObj) {State = value;} }
    }

    public void Start()
    {
        this.state = 0;
        this.mainHandler.BeginInvoke(null, null);
    }
}

public Main()
{
    Monitor firstMonitor = new Monitor(20);
    firstMonitor.Start();
    do
    {
        if(firstMonitor.state == 1) WriteLine("DB 20 stop working");
    } while(true);
}

Проблема, с которой я столкнулся с обработкой исключений, если функция MonitorHandler выдает исключение, у меня нет способа узнать это.

Я не вызываю EndInvoke, поэтому исключение не перебрасывается в основной поток.

Моя цель - проверить состояние БД, просто взломав поле состояния экземпляра монитора. Если исключение в throwen, мне нужно каким-то образом «перенести» это исключение в основной поток, но я не хочу начинать проверку состояния и статуса делегата Monitor.

Я очень люблю находить путь к Монитор самого потока (тот, который активирован .BeginInvoke), чтобы вызвать исключение в основном потоке.

Спасибо.

1 Ответ

2 голосов
/ 20 января 2020

Мне очень нравится находить путь к самому потоку монитора (тот, который активирован .BeginInvoke), чтобы генерировать исключение в главном потоке.

За исключением чего-то другого подобно ThreadAbortException, отсутствует механизм для вставки исключения в другой произвольный поток.

Если вы собираетесь использовать метод делегата BeginInvoke() и хотите перехватить исключение в потоке, отличном от того, где сам делегат вызывается, тогда вам нужно будет вызвать EndInvoke() в этом потоке.

Другим вариантом будет явное и ручное обращение с исключением. Т.е. перехватить исключение с помощью try / catch в рабочем потоке, а затем использовать явно определенный механизм по вашему выбору (например, ConcurrentQueue<T>) для передачи перехваченного исключения в код, выполняющийся в основном потоке.

Все это говорит о том, что использование метода BeginInvoke() делегата никогда не было таким идеальным способом выполнения кода асинхронно, как сейчас, и сегодня это даже хуже идеи. Из вашего вопроса не ясно, какова природа «основного потока», не говоря уже о том, имеет ли этот поток контекст синхронизации. Но если предположить, что это так (например, это поток GUI или контекст ASP. NET и т. Д. c.), То желаемое поведение легко реализуется с помощью Task.Run() для запуска асинхронной операции, а затем с помощью await в главном потоке, чтобы зафиксировать завершение этой операции, вместе с любым сгенерированным исключением.

В этом отношении, даже если ваш основной поток в настоящее время не имеет контекста синхронизации, это может быть правильный подход - дать ему один. Либо используя один из существующих механизмов, либо написав свой собственный. Это было бы хорошей идеей, если, например, вы ожидаете, что в коде часто встречается сценарий «распространение исключения из рабочего потока в основной поток» в коде. Это позволит вам использовать встроенную языковую поддержку для решения этой проблемы (т. Е. async / await), вместо того, чтобы использовать что-то для каждого экземпляра. Реализация контекста синхронизации не тривиальна, но это работа, которую вы можете выполнить один раз, а затем снова и снова использовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...