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.