te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c# - IsCancellationRequested taking a long time to update after Cancel() - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - IsCancellationRequested taking a long time to update after Cancel() - Stack Overflow

programmeradmin3浏览0评论

I'm noticing some strange behaviour with CancellationTokenSource wherein when I call the Cancel() method, IsCancellationRequested takes some time to update.

I am not sure if this is the cause, but in case it's relevant, it's being used in relation to a BluetoothLEAdvertisementWatcher.

Here are some of the relevant snippets to illustrate how it's being used (the entire code structure would be much too complex).

So take the following class, AdvertisementHandler :

    internal AdvertisementHandler(ILogger logger)
    {
        _logger = logger;

        _adWatcher = new BluetoothLEAdvertisementWatcher();
        _adWatcher.Received += OnAdvertisementReceived;
        _adWatcher.Stopped += OnAdvertisementWatcherStopped;
    }

    internal void StartWatching(CancellationToken token)
    {
        _cancellationToken = token;
        _adWatcher.Start();
    }

    internal void StopWatching()
    {
        _adWatcher.Stop();
    }

    private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
    {
        
        if (_cancellationToken.IsCancellationRequested)
        {
            _logger.LogDebug($"Cancelling device search");

            StopWatching();
            return;
        }

        _logger.LogDebug($"Received an advertisement");

        // Properly handle the advertisement.

Then in a separate class I have something like...

    private void DetectNewDevices()
    {
        try
        {
            _cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(120));

            _logger.LogDebug($"Looking for new {deviceName} devices");

            _adHandler.StartWatching(_cancellationSource.Token);

            Thread.Sleep(250);

        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
        }
    }

    public bool ChooseDevice(IDeviceInstance deviceInstance)
    {
        if (_cancellationSource != null)
        {
            _logger.LogDebug("Choosing A device so cancelling search.");
            _cancellationSource.Cancel();
        }

        // Do some other stuff.
    }

First, DetectNewDevices() gets called, which calls StartWatching() and passes to it the cancellation token. This causes the advertisements to start flowing in (Received event fires on each advertisement.)

Then later, when I hit the ChooseDevice() function, it goes in and calls _cancellationSource.Cancel(); (I can see it in the log). However, it can take quite a bit of time, in one case almost 2 mins, before it acknowledges _cancellationToken.IsCancellationRequested as being true.

Note that this is not simply because it took that long before that event handler was next called - I have received many advertisements in the mean time (i.e. "Received an advertisement" appears in the log) and even put a breakpoint in there and seen that IsCancellationRequested is false. Eventually it does change to true, but why not instantly after calling Cancel()?

I've tagged "bluetooth-low-energy" and "uwp" in case this is just a quirk of that framework.

I'm noticing some strange behaviour with CancellationTokenSource wherein when I call the Cancel() method, IsCancellationRequested takes some time to update.

I am not sure if this is the cause, but in case it's relevant, it's being used in relation to a BluetoothLEAdvertisementWatcher.

Here are some of the relevant snippets to illustrate how it's being used (the entire code structure would be much too complex).

So take the following class, AdvertisementHandler :

    internal AdvertisementHandler(ILogger logger)
    {
        _logger = logger;

        _adWatcher = new BluetoothLEAdvertisementWatcher();
        _adWatcher.Received += OnAdvertisementReceived;
        _adWatcher.Stopped += OnAdvertisementWatcherStopped;
    }

    internal void StartWatching(CancellationToken token)
    {
        _cancellationToken = token;
        _adWatcher.Start();
    }

    internal void StopWatching()
    {
        _adWatcher.Stop();
    }

    private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
    {
        
        if (_cancellationToken.IsCancellationRequested)
        {
            _logger.LogDebug($"Cancelling device search");

            StopWatching();
            return;
        }

        _logger.LogDebug($"Received an advertisement");

        // Properly handle the advertisement.

Then in a separate class I have something like...

    private void DetectNewDevices()
    {
        try
        {
            _cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(120));

            _logger.LogDebug($"Looking for new {deviceName} devices");

            _adHandler.StartWatching(_cancellationSource.Token);

            Thread.Sleep(250);

        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
        }
    }

    public bool ChooseDevice(IDeviceInstance deviceInstance)
    {
        if (_cancellationSource != null)
        {
            _logger.LogDebug("Choosing A device so cancelling search.");
            _cancellationSource.Cancel();
        }

        // Do some other stuff.
    }

First, DetectNewDevices() gets called, which calls StartWatching() and passes to it the cancellation token. This causes the advertisements to start flowing in (Received event fires on each advertisement.)

Then later, when I hit the ChooseDevice() function, it goes in and calls _cancellationSource.Cancel(); (I can see it in the log). However, it can take quite a bit of time, in one case almost 2 mins, before it acknowledges _cancellationToken.IsCancellationRequested as being true.

Note that this is not simply because it took that long before that event handler was next called - I have received many advertisements in the mean time (i.e. "Received an advertisement" appears in the log) and even put a breakpoint in there and seen that IsCancellationRequested is false. Eventually it does change to true, but why not instantly after calling Cancel()?

I've tagged "bluetooth-low-energy" and "uwp" in case this is just a quirk of that framework.

Share Improve this question edited 2 days ago komodosp asked 2 days ago komodospkomodosp 3,6464 gold badges36 silver badges68 bronze badges 8
  • 1 Are you able to reproduce this in a minimal reproducible example? I wouldn't expect the BLE or UWP aspects to be relevant - I'd expect you to be able to reproduce this in a simple console app which we could run. – Jon Skeet Commented 2 days ago
  • 1 Cancellation is cooperative, ie operation itself, if it has been already started, should explicitly check for cancellation (and your sample does it), but how often OnAdvertisementReceived gets called? Presumably this implementation just has such sort of tardish reactivity. If client code has access to internal StartWatching, it has access to StopWatching too, correct? Why then not to call StopWatching immediately upon choosing device? – Ryan Commented 2 days ago
  • @Ryan - this Plan B. This is kind of a simplified version of the code, actually there are a few classes in between the two I've posted there, and currently it doesn't have access to StopWatching() - it would take a bit of restructuring to do this but I was hoping not to have to. In answer to your other question, OnAdvertisementReceived gets called several times between the calling of Cancel() and IsCancellationRequested having a true value. – komodosp Commented 2 days ago
  • @JonSkeet - funnily enough, no! I've created a similar setup in a console app just using a random timer to fire the event, and it always has IsCancellationRequested = true on the firing following the call to Cancel(). – komodosp Commented 2 days ago
  • 3 If this isn't actually representative of the code, then that makes me suspect that there could be something in between that means you're not testing the cancellation token that's come directly from the cancellation token source that you're cancelling. I would strongly recommend you start with the failing code and remove bits of it (while still seeing it fail) until you've either figured out the disconnect or got a short but complete example you can provide us with. Only providing code that may or may not actually demonstrate the problem doesn't really help. – Jon Skeet Commented 2 days ago
 |  Show 3 more comments

1 Answer 1

Reset to default 2

I feel a little foolish now, not sure whether to delete this question!

Anyway, the problem, as it turns out was, as JonSkeet suggested, something in between that wasn't included in my snippets above.

In my UI class I had been using the same CancellationTokenSource for two different tasks. If the user started either one, a new instance was created its token used.

So sometimes, the token was passed into the controller class, but then reinitialised in the UI class - then presumably when I cancelled, it would be cancelling the new instance, while the previous one became an orphan.

Then, since I had put a 2-minute timeout on it, the original one would cancel itself, which I misinterpreted as a delay between my cancelling and the token receiving the cancellation.

Working on a solution right now that requires a bit of restructuring, but the main problem was my re-instantiating the token source each time I began a new task and fetting about the old one.

发布评论

评论列表(0)

  1. 暂无评论