Безопасно ли вызывать метод обратного вызова таймера, как это? - PullRequest
0 голосов
/ 28 декабря 2018

Пожалуйста, исправьте меня, если у меня есть некоторые ошибки в этой логике (не такие элегантные вещи, как избавление от инициализации конструктора и использование метода Init вместо Poll).До сих пор у меня не было опыта с обратными вызовами по таймеру.Надеюсь, код довольно понятен.Что меня смущает, так это некоторая смесь асинхронных вещей (например, создание клиента подключения) и дальнейшего кода - хотя я просто повторно использовал класс IClient, он не мой):

    public async Task<WaitForLoanActivationDto> WaitForLoanActivation(string userName, string accountGuid, int timeout)
    {
        const int dueTime = 0;
        const int pollPeriod = 500;
        Poll<WaitForLoanActivationDto> state = new Poll<WaitForLoanActivationDto>
        {
            Client = await _rpcConnectionPool.GetClientAsync(userName),
            AutoResetEvent = new AutoResetEvent(false),
            StartTime = DateTime.Now,
            Timeout = timeout,
            Parameters = new Variant[] { accountGuid },
            Result = new WaitForLoanActivationDto { Active = false }
        };

        Timer timer = new Timer(new TimerCallback(WaitForLoanActivationCallback), state, dueTime, pollPeriod);
        state.AutoResetEvent.WaitOne();
        timer.Dispose(state.AutoResetEvent);

        if (state.ThreadException != null)
        {
            throw state.ThreadException;
        }

        return state.Result;
    }

    private void WaitForLoanActivationCallback(object state)
    {
        Poll<WaitForLoanActivationDto> pollState = (Poll<WaitForLoanActivationDto>)state;

        if (pollState.StartTime.AddMilliseconds(pollState.Timeout) >= DateTime.Now)
        {
            try
            {
                using (RPCReply reply = ResultHelper.Check(pollState.Client.ExecuteRemoteCommand(WaitForLoanActivationRpcName, pollState.Parameters)))
                {
                    pollState.Result.Active = reply[2].IDList["Active"].AsBoolean().Value;
                    VariantList statusList = reply[2].IDList["statuses"].List;
                    if (statusList.Count > 0)
                    {
                        var statuses = CustomerInformationConverter.GetStatusesList(statusList);
                        pollState.Result.Statuses = statuses.ToArray();
                    }
                    if (pollState.Result.Active)
                    {
                        pollState.AutoResetEvent.Set();
                    }
                }
            }
            catch (Exception ex)
            {
                pollState.Result = null;
                pollState.ThreadException = ex;
                pollState.AutoResetEvent.Set();
            }
        }
        else
        {
            pollState.AutoResetEvent.Set();
        }
    }

1 Ответ

0 голосов
/ 28 декабря 2018

Спасибо, ребята.@ckuri, основываясь на твоей идее, я придумал код ниже.Я не использовал Task.Delay, потому что, как я понял, это создает задержку, даже если задача успешно завершена - после нее.Цель моего случая - запускать метод RPC каждые pollPeriod миллисекунды в течение timeout миллисекунд.Если метод возвращает Active == false - продолжить опрос, в противном случае - вернуть результат.Время выполнения RPC может занять более pollPeriod миллисекунд, поэтому, если он уже запущен - нет смысла создавать другую задачу.

    public async Task<WaitForLoanActivationDto> WaitForLoanActivation(string userName, string accountGuid, int timeout)
    {
        var cancellationTokenSource = new CancellationTokenSource();
        try
        {
            const int pollPeriod = 500;
            IClient client = await _rpcConnectionPool.GetClientAsync(userName);
            DateTime startTime = DateTime.Now;
            WaitForLoanActivationDto waitForLoanActivationDto = null;

            while (startTime.AddMilliseconds(timeout) >= DateTime.Now)
            {
                waitForLoanActivationDto = await Task.Run(() => WaitForLoanActivationCallback(client, accountGuid), cancellationTokenSource.Token);
                if (waitForLoanActivationDto.Active)
                {
                    break;
                }
                else
                {
                    await Task.Delay(pollPeriod, cancellationTokenSource.Token);
                }
            }

            return waitForLoanActivationDto;
        }
        catch (AggregateException ex)
        {
            cancellationTokenSource.Cancel();
            throw ex.InnerException;
        }
    }

    private WaitForLoanActivationDto WaitForLoanActivationCallback(IClient client, string accountGuid)
    {
        using (RPCReply reply = ResultHelper.Check(client.ExecuteRemoteCommand(WaitForLoanActivationRpcName, accountGuid)))
        {
            var waitForLoanActivationDto = new WaitForLoanActivationDto
            {
                Active = reply[2].IDList["Active"].AsBoolean().Value
            };
            VariantList statusList = reply[2].IDList["statuses"].List;
            if (statusList.Count > 0)
            {
                var statuses = CustomerInformationConverter.GetStatusesList(statusList);
                waitForLoanActivationDto.Statuses = statuses.ToArray();
            }
            return waitForLoanActivationDto;
        }
    }
...