【问题标题】:Is it safe to call timer callback method like this?像这样调用定时器回调方法是否安全?
【发布时间】:2018-12-27 23:19:25
【问题描述】:

如果我在这个逻辑中有一些错误,请纠正我(不是一些优雅的事情,比如摆脱构造函数初始化并使用 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();
        }
    }

【问题讨论】:

  • 我会放弃计时器,以获得完整的异步解决方案,使用 Task.Delay 循环进行轮询。这也将消除 AutoResetEvent 的必要性并使代码更清晰。此外,由于 AutoResetEvent 并且您在等待它,您也阻塞了一个线程,我猜它可能会停止您的 UI,而 Task.Delay 不会出现这种情况。
  • 是的,我必须同意这段代码相当做作,你正在创建一个状态机,GetClientAsync 然后启动一个计时器并阻塞调用线程,同时计时器创建新线程来做某事或其他事情,完成后,它让原始线程继续。似乎它是从旧式代码重构而来的

标签: c# timer exception-handling async-await autoresetevent


【解决方案1】:

谢谢你们。 @ckuri,根据您的想法,我想出了下面的代码。不过,我没有使用Task.Delay,因为据我所知,即使任务成功完成,它也会产生延迟——在它之后。我的案例的目标是在timeout 毫秒内每pollPeriod 毫秒运行一次RPC 方法。如果该方法返回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;
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-08
    • 2015-10-05
    • 1970-01-01
    • 2021-02-21
    相关资源
    最近更新 更多