On Wed, Jan 12, 2022 at 11:03 AM jan.f...@gmail.com <jan.flyb...@gmail.com> wrote:
> Just a related observation. I don't think you need to have a select > statement to check if a context is cancelled. I think it is sufficient to > just check if ctx.Err() gives a result different from nil. > I believe you are right, the select-statement I wrote should be equivalent to just `if err := ctx.Err(); err != nil { return err }`. > > From the API documentation for Context (https://pkg.go.dev/context#Context > ): > // If Done is not yet closed, Err returns nil. > // If Done is closed, Err returns a non-nil error explaining why: > // Canceled if the context was canceled > // or DeadlineExceeded if the context's deadline passed. > // After Err returns a non-nil error, successive calls to Err return the > same error. > Err() error <https://pkg.go.dev/builtin#error> > > //Jan > > onsdag 12 januari 2022 kl. 09:41:23 UTC+1 skrev lege...@gmail.com: > >> 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.wa...@googlemail.com> >> wrote: >> >>> >>> >>> On Wed, Jan 12, 2022 at 3:01 AM E Z <lege...@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...@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/5ce0f16b-1e8b-4582-8307-55fd8e60df15n%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/5ce0f16b-1e8b-4582-8307-55fd8e60df15n%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/CAEkBMfEVk_5d%2BK9Z1SHz4s2bpycpiv83r5K-_hie69K9zj61HQ%40mail.gmail.com.