Some understanding of coroutine

original
2016/12/09 18:11
Reading 9.4K

Coroutine
The coroutine was first proposed and implemented by Melvin Conway in 1963. In a word, it defines: The coroutine is a lightweight thread in user mode

Threads and coroutines
Threads and coroutines are often compared together; Once a thread is created, the writer cannot decide when to obtain or release the time slice, which is uniformly scheduled by the operating system; For the programmer, the coroutine can control the time of switching, and the switching cost is less than that of threads, because there is no need to switch in kernel mode.
The coroutine avoids meaningless scheduling, which can improve performance. However, the programmer must assume the responsibility of scheduling. At the same time, the coroutine also loses the ability of standard threads to use multiple CPUs, but it can make full use of multiple CPUs through multiple (processes+multiple coroutines) modes.
Another important feature of coroutines is that they act in the user mode, and the operating system kernel has no awareness of coroutines. In this way, the creation of coroutines is similar to the creation of ordinary objects, which is very lightweight, so that you can easily create hundreds of thousands of coroutines in a thread, just like hundreds of thousands of function calls. Imagine how terrible it would be if hundreds of thousands of threads were created in one process.

Process, thread, coroutine
Process: It has its own independent heap and stack, neither sharing the heap nor the stack. The process is scheduled by the operating system.
Threads: It has its own independent stack and shared heap. The shared heap does not share the stack. Threads are also scheduled by the operating system (standard threads are).
Coprogram: Like threads, it shares the heap and does not share the stack. The programmer displays the scheduling in the code of the coprogram.

Support for the cooperation program
Native support coroutine for Lua, Go, C # and other languages
Java relies on third-party libraries, such as the most famous collaboration open source library, Kilim
The functions setjmp and longjmp in the C standard library can be used to implement a coroutine.

Let's take the Lua language as an example to understand the working mechanism of the coroutine

 function foo(a) print("foo", a) return coroutine.yield(2 * a) end co = coroutine.create(function ( a, b ) print("co-body_01", a, b) local r = foo(a + 1) print("co-body_02", r) return "end" end) print("---main---", coroutine.resume(co, 1, 10)) print("---main---", coroutine.resume(co, "r7"))

Operation results:

 D:\>luac text.lua D:\>lua luac.out co-body_01      1       10 foo     2 ---main---      true    4 co-body_02      r7 ---main---      true    end

The two functions, resume and yield, are mainly used to control the switching time. The specific description is shown in the following figure (source network):

The coroutine is often used to yield the CPU directly when encountering IO blocking operations, so that the following programs can continue to execute, and then resume to the previous yield location after the operation is completed; Do you think this mode is similar to the one we have encountered Asynchronous callback The modes are a little similar. Here's a comparison.

Co scheduling and callback
The coroutine is often used to compare with the callback because it implements the function of asynchronous communication; Here is a specific scenario to describe the difference between the two:
First, A should walk in front of B, and then A says "Hello" to B, and after A says "Hello", B says "Hello" to A. Note that there is a delay before each action here
If this scenario is described in the way of callback, it will be like this:

 A.walkto(function (  ) A.say(function (  ) B.say("Hello") end, "Hello") end, B)

Only 2 layers of nesting are used here. If there are more layers, it is really non-human code. If you use coroutine to implement:

 co = coroutine.create(function (  ) local current = coroutine.running A.walto(function (  ) coroutine.resume(current) end, B) coroutine.yield() A.say(function (  ) coroutine.resume(current) end,  "hello") coroutine.yield() B.say("hello") end) coroutine.resume(co)

The structure is much clearer. The coroutine enables programmers to write asynchronous large code in a synchronous manner;
A summary on the source website: Let non-human code originally written in asynchronous+callback mode be written in a seemingly synchronous way

Whether it's a coroutine or a callback, it actually provides an asynchronous non blocking programming mode. Let's take a look at Java's attempts in this mode:

Java asynchronous non blocking programming mode
The java language itself does not provide support for coroutines, but some third-party libraries provide support, such as kilim in the early days of the JVM and Quasar, which is more mature now. However, it is not intended to introduce kilim and quasar frameworks here; Here we will introduce the Future class in java5 and the CompleteableFuture class in java8.

1. Future use

 ExecutorService es = Executors.newFixedThreadPool(10); Future<Integer> f = es.submit(() ->{ //Long time asynchronous computing // …… //Then return the result return 100; }); //while(!f.isDone()) f.get();

Although Future and its related usage methods provide the ability to execute tasks asynchronously, it is inconvenient to obtain the results. The results of tasks can only be obtained by blocking or polling.
This mode is temporarily called Pseudo asynchrony In fact, what we want is a model similar to Netty:

 ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { // SUCCESS } else { // FAILURE } } });

The callback method is automatically called when the operation is completed, and the CompleteFuture class is finally introduced in java8

2. Use of CompleteableFuture
CompletableFuture provides a very powerful extension function of Future, which can help us simplify the complexity of asynchronous programming, provide the ability of functional programming, process the calculation results through callbacks, and provide a method to transform and combine the CompleteFuture. I don't want to introduce more about CompleteFuture here. I want to know more Introduction to CompleteableFuture See a common usage scenario:

 CompleteFuture<Integer>future=CompleteFuture. supplyAsync (time consuming function); Future<Integer> f = future.whenComplete((v, e) -> { System.out.println(v); System.out.println(e); }); System.out.println("other...");

CompleteableFuture truly implements the asynchronous programming mode

summary
Why are coroutines so rare in Java? Basically all libraries in Java are blocked synchronously, and asynchronous non blocking is rarely seen. And thanks to J2EE and the brainwashing of the three frameworks (SSH) on Java, most Java programmers have been accustomed to completing a business logic linearly based on threads, which makes it difficult for them to accept an asynchronous programming model that separates logic.

Personal blog: codingo.xyz

Expand to read the full text
Loading
Click to join the discussion 🔥 (16) Post and join the discussion 🔥
Reward
sixteen comment
one hundred and eight Collection
five fabulous
 Back to top
Top