这样调用计时器回调方法是否安全?

时间:2018-12-27 23:19:25

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

如果我在此逻辑中有一些错误(请注意纠正一些错误(没有一些繁琐的事情,例如摆脱构造函数的初始化,而使用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 :(得分:0)

谢谢你们。 @ckuri,根据您的想法,我想到了以下代码。不过,我没有使用Task.Delay,因为据我了解,即使任务成功完成,它也会造成延迟。我的案例的目的是在pollPeriod毫秒内每timeout毫秒运行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;
        }
    }