1

I consume a library that exposes a set as a foreach similar to how a BlockingCollection does, where my enumerator will wait until the next item is provided.

foreach(var item in items)
{
     ...
}

My consumer will wait on the foreach until the next item is provided. It does not enter the block until ready. This is fine, but I'd like to be able to intentionally stop enumerating under certain conditions even if I'm currently waiting.

I'm not asking about break inside the block. Exiting the loop when I'm in the block is trivial. I want to "cancel" the enumeration from a different thread.

Matthew
  • 10,244
  • 5
  • 49
  • 104
  • Might or might not be a duplicate of http://stackoverflow.com/q/23295119/11683 – GSerg May 20 '16 at 19:09
  • 1
    I think it would help to clarify what you mean by "stop enumerating". Since the code is stopped, blocking on the next item, I can only assume you are talking about canceling a thread's blocked enumeration from a different thread. Or do you have something else in mind? – 31eee384 May 20 '16 at 19:43
  • Since you write that you consume the library, I assume you have no control over the implementation of the `items` part of your code. In that case I don't think there is an elegant option. Other than adding another indirection layer that you can pull the rug out underneath, which will be clunky in a best case scenario - or outright dangerous, if you start terminating threads - your options are very limited. – Frank J May 20 '16 at 20:22
  • Perhaps you can use reactive extensions. It will use a scheduler internally, so no implementation is needed there. Simply dispose the subscriber if you don't care about the next result anymore. – Caramiriel May 20 '16 at 20:31
  • @FrankJ you're right I don't. I can, of course, just terminate the consuming process but that seems rather inelegant to me. – Matthew May 20 '16 at 21:00
  • @31eee384 You're right. I want to cancel the blocked enumeration from a different thread. – Matthew May 20 '16 at 21:00
  • If you cannot ask that library's author to provide proper cancellation support, and library does not provide any means to cancel pending enumerations (like disposing something) - most likely you won't find elegant solution. – Evk May 20 '16 at 21:07

1 Answers1

1

It is not pretty, but you could wrap this inside a BackgroundWorker or other threading technique.

I do not know what your BlockingCollection is, but I will assume for the sake of the code below that it consists of a BlockingItem Collection.

private bool _someExternalFlag;

private void Test(BlockingCollection items)
{
    _someExternalFlag = false;
    using (var worker = new BackgroundWorker())
    {
        worker.WorkerReportsProgress = true;
        worker.WorkerSupportsCancellation = true;
        worker.DoWork += delegate
        {
            var index = 0;
            foreach (var item in items)
            {
                if (worker.CancellationPending)
                {
                    break;
                }
                index++;
                // ... process your item here or by passing it to the main thread
                worker.ReportProgress(index, item);
            }
        };
        worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
        {
            if (_someExternalFlag)
            {
                worker.CancelAsync();
            }
            else
            {
                var item = e.UserState as BlockingItem;
                // use item if you need it
            }
        };
        worker.RunWorkerAsync();
    }
}

Your BackgroundWorker's DoWork method is not going to take up any CPU resources while it is waiting for the next BlockingItem to be released.

The variable I defined _someExternalFlag could be anything that is set when a user clicks a Cancel Button or set when the Form is closed.