Hi Axel, thank you very much, your explanation of this problem is very
detailed and solves my question.

What I think it's not so good is that I must add the select at all the
places the goroutine is waiting or looping, it makes the execution and
scheduling part of the task scheduling system coupling a bit tightly,
because I need to implement many different types of tasks, and I have to
consider the cancel logic (which I think belongs to scheduling part)
explicitly in each type of task executor, instead of leaving it all to the
scheduling part. I think it will be better if the task executor can only
consider the business logic.

I also tried to abstract this cancel logic into generic processing so that
it could be easily applied to different types of tasks, but I didn't find a
good way to do this, I had to manually add the code wherever it was needed

On Tue, Jan 11, 2022 at 10:31 PM Axel Wagner <axel.wagner...@googlemail.com>
wrote:

>
>
> On Wed, Jan 12, 2022 at 3:01 AM E Z <legend...@gmail.com> wrote:
>
>> Thank you very much.
>>
>> I understand that we can use context.Context to resolve the network
>> blocking problem in long-running function if the network library support
>> passing a context parameter.
>>
>> But for the CPU-bound code,  Is the following implementation mentioned by
>> axel the only way to make a function exit earlier?
>>
>
> It's not the only way, but it's the way I'd generally recommend.
> Universally using `context.Context` to signal cancellation solves exactly
> the problem you where having. Specifically,
>
> > The above code is executing in a goroutine, if I want to cancel this
> goroutine, I can send a signal to task.channel, but the signal only can be
> retrieved after the task.task.Run is finished, it may be a long time, such
> as 5 mins.
>
> If `task.task.Run` takes a `context.Context`, it can exit sooner than
> after 5 minutes. If it takes that long because it does remote requests, it
> can propagate the Context itself. If it is CPU-bound, it can check if the
> Context was cancelled, say, every 1000 iterations (or whatever. What's a
> reasonable number depends heavily on what it's doing).
>
> But, yes, for such a CPU-bound task, actively checking if it was cancelled
> via a mechanism like a Context is the only way to be aborted.
>
> For example, goroutine is executing a task to update a DNS record and then
>> wait some time until the DNS record takes effect in some name servers.  It
>> may take some seconds even minutes to make the DNS record take effect in
>> the name server.
>>
>
> To be clear, this is not a CPU-bound process. Updating the DNS record is
> either a network request/IPC. The waiting is then a loop like
>
> for {
>     select {
>     case <-ctx.Done():
>         return ctx.Err()
>     case <-time.After(time.Second()): // simplistic, you'd likely want
> some jitter and/or exponential backoff here
>         if recordHasChanged(ctx) { // network request to check if the DNS
> record has changed - takes a Context, as it's a network request
>             return nil
>         }
>     }
> }
>
> This will spend most of its time sleeping.
>
> A CPU-bound task is something like a diff-operation, which is just an
> algorithm that can be very slow for large inputs, just because it has a lot
> of work to churn through.
>
> In this case, seems I can't cancel the running goroutine except that we
>> add the above select at every for loop or wait timer, or  I change the
>> design to split these time-consuming operations into different goroutine.
>> Both seem not so good.
>>
>
> I don't understand why you think this is not good. It seems perfectly
> reasonable code. But yes, it's what you have to do. Go has no way to
> asynchronously stop code, you need to manually cancel. And context.Context
> gives a universal mechanism to do that, which I would recommend using for
> that.
>
>
>>
>> On Tuesday, January 11, 2022 at 1:04:15 PM UTC-8 Ian Lance Taylor wrote:
>>
>>> On Tue, Jan 11, 2022 at 12:15 PM 'Axel Wagner' via golang-nuts
>>> <golan...@googlegroups.com> wrote:
>>> >
>>> > The best way to do this is to plumb a context.Context through all
>>> long-running functions - in particular, anything talking to the network.
>>> > Most RPC and network frameworks provide a way to pass a Context, so
>>> consistently doing this will more or less transparently cancel your
>>> business logic ASAP.
>>> > For purely CPU bound code, this is a bit more awkward, because you
>>> indeed have to intersperse code like
>>> > select {
>>> > case <-ctx.Done():
>>> > return ctx.Err()
>>> > default:
>>> > }
>>> > to make the code return early. But that should be relatively rare.
>>>
>>> Yes. See also https://go.dev/blog/context .
>>>
>>> Ian
>>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "golang-nuts" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to golang-nuts+unsubscr...@googlegroups.com.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/golang-nuts/d1898f67-d1e0-4276-af92-016b82866de4n%40googlegroups.com
>> <https://groups.google.com/d/msgid/golang-nuts/d1898f67-d1e0-4276-af92-016b82866de4n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAO%3DFurt2JhezuRcRPVEKXd8eDTeu0hqwnJ%3Dr-aQQSqfYcDkdvQ%40mail.gmail.com.

Reply via email to