Coroutine (coroutine) mode and code reuse of "control" and "behavior"

Source: Internet
Author: User
ArticleDirectory
    • Create coroutine
    • Coroutine status
Concept

The concept of coroutine was Melvin at the earliest.
Conway proposed in 1963 is the concept of concurrent computing. It refers to two sub-processes that work together to complete a task and use it to implement collaborative multi-task. coroutine technology is essentiallyProgramControl mechanism. For example, if you are a consumer or producer, you can take a few steps.
Coroutine can be divided:

    • Asypolicric coroutine, or semi-asypolicric, or semi-coroutine)
    • Symmetric Ric coroutine.

Asypolicric coroutine is called asymmetric because it provides two types of operations that pass program control: one is to call coroutine (via coroutine. resume); the other is to suspend the coroutine and return control of the program to the cooutine caller (via coroutine. yield ). An asymmetric coroutine can be seen as its caller. The relationship between the two is very similar to that between the routine and Its caller. Symmetric coroutine is characterized by only one operation (coroutine. Transfer) that transmits control directly to the specified coroutine. There was a saying that the ability of symmetric and asymmetric coroutine mechanisms is not equivalent, but in fact it is easy to implement the latter based on the former. Many dynamic scripting languages (Python, Perl, Lua, Ruby) provide coroutine or similar mechanisms.
The symmetric coroutine mechanism can directly specify the target of control transfer and possess great freedom. However, the cost of such freedom is to sacrifice the program structure. If the program is a little more complex, it is difficult for a very experienced programmer to have a comprehensive and clear grasp of the program process. This is very similar to the GOTO statement, which allows the program to jump to any place you want to go, but it is hard for people to understand the program full of Goto. Asymmetric coroutines have a good hierarchical relationship. (re) starting these coroutines is very similar to calling a function: The coroutines started by (re) Get control and start execution, then suspend (or terminate) and return the control to the coroutine caller. This is exactly the same as the structured programming style.

Similarities and differences between threads and threads

A coroutine is similar to a thread where each coroutine has its own stack and local variables.

The main difference between threads and threads is:
1. Threads can run concurrently. global variables (write conflicts) cannot be written between threads ).
2. coroutine cannot run concurrently, and global variables can be shared between coroutine (no write conflict exists ).

Implementation and use of coroutine to create coroutine
    type   tmecoroutinefunc  =  procedure   (  const   ACO routine: tmecoroutine); 
tmecoroutinemethod = procedure () of Object ;< br> var FUNC: tmecoroutinefunc;
Co = tmecoroutine. create (func)

A parameter is a function, and the return value is the created coroutine object.

Coroutine status

The coroutine has three statuses: suspend, run, and stop ).
When we create a coroutine, it starts in the suspended state, that is, it does not run automatically when we create the coroutine.

 
  St=CO. status;

Activate coroutine

 
  Issucessful:=CO. Resume ();

Activate the suspended coroutine to continue running. The CO parameter is a coroutine object.

If the coroutine is suspended, it continues to run. The Resume function returns true. If the coroutine has been stopped or another error occurs, the Resume function returns false.

Suspend coroutine

  CO. Yield ([...]);

Suspend the current coroutine. Until the coroutine is activated again by the external coroutine using coroutine. Resume, it is returned to the place where the coroutine. yield function is executed to continue execution.
The parameter of coroutine. yield will be passed to the saveyieldedvalue virtual method. You need to reload this method for processing.

When a coroutine is running, it cannot be terminated externally. You can only call coroutine. Yield within the coroutine to suspend the current coroutine.
You do not need to consider the issue of coroutine security and coroutine synchronization. Coroutine'sCodeIt is easier to write than the thread code.

Reuse of "control" and "behavior"

In many cases, we need to traverse the elements in the data structure (such as list and stack) according to certain requirements, which is called "control" and then perform an operation on the target element (such, show this element), which is called "action ". In many cases, code for such "control" or behavior can be reused, but it is difficult to separate "control" from "behavior, as a result, we have to write these similar code over and over again (although callback can be used to achieve a certain degree of "control" and separation of behavior, but not elegant, it cannot be completely reused ).

Production and Consumption

Let's take a look at the following code. The producer process (producer) generates some values (which can be traversed as required), while the consumer process (consumer) processes the values (operations on the target element ):

  Procedure  Producer ();
VaR I: integer; Begin For I: = 0 To 100 Do If I mod 5 = 0 Then Consumer (I );
End ;
Procedure Consumer ( Const Value: integer );
Begin Writeln (value ); End ;

Note that coupling occurs in the consumer process called by the producer. The producer can only serve the cosumer process. We hope that the producer process (producer) can enhance versatility, reduce coupling, and serve different consumers.

OK. We thought of callback:

  Type  Tcomsumercallback:  Procedure  (  Const  Value: integer );
Procedure Producer ( Const Acallback: tcomsumercallback );
VaR I: integer; Begin For I: = 0 To 100 Do // Cyclic enumeration Control If I mod 5 = 0 Then Acallback (I );
End ;

Now, the producer can serve different consumers. However, new problems arise. What if we still want to call the producer to obtain the value only when the consumer requires the value? Implements a thorough separation of control and behavior. Like this:

  Procedure  Mainconsumer ();
Begin // Control in myproducer and control reuse For I In Myproducer Do // Called only when the consumer requires a value Begin Consumer (I );
.... // The value of the slave producer can be stopped at any time. End ;
End ;
Visitor mode and iterator Mode

The essence of callback is a simple visitor mode, which is simple because it only has the visitor and does not have the visited part. It is difficult to use callback: The producer gives the callback only when the consumer wants it. If the consumer does not ask, the producer does not answer the question. This is determined by the features of the visitor mode: the callback user throws the callback function to traverse.AlgorithmAnd then run the algorithm, pray and wait for the completion of the algorithm (push
And
Wait), the user has completely lost control, and can only regain control after the algorithm is completed or aborted. Although the iterator mode can easily achieve this (iterator is essentially a Q & A mode, or a consumer/producer mode, the iterator usage itself is lazy, the traversal algorithm stops there waiting for the dispatch of the iterator user), but the consumer cannot be reused if the callback method is abandoned. It is almost impossible to achieve "reuse" control "and" reuse "behavior" at the same time (of course, if you only want to achieve "consumer needs, producers can give, but it is more difficult than callback, and it cannot be used in a variety of data structures), because the visitor mode and iterator mode have exactly the opposite features:
*
Iterator is an active model, PULL model, ask and get. Iterator waits for user dispatch.
*
Vistor is a passive model, PUSH model, plugin/callback model, push and pray and wait. Visitor listens to the dispatch of the algorithm.

  // Use the iterator mode to set the value as per the consumer's needs: for simple arrays and linked lists, you only need to save the current call steps (array index,
// Or the structure of the current pointer) and the calling environment (internal dataset), and return it to the user. // Every time you call iterator. Next, iterator moves the index or pointer backward. If the tree contains complex internal data,
// The graph structure is quite complicated. For example, if a tree is traversed and the node of the tree does not contain a parent reference, the iterator
// You must maintain a stack to save all the preceding parent nodes. Type Tproducer = Class Private I: integer; Public Current: integer; Function Movenext: Boolean;
End ;
Procedure Tproducer. movenext;
VaR
I: integer;
Begin Result: = False;
If I <= 100 Then
Begin
  If I mod 5 = 0 Then Current: = I;
I: = I + 1 ;
Result: = True;
End ;
End ;
Procedure Mainconsumer ();
Begin
With Tproducer. Create Do
Try
While Movenext Do Begin Consumer (current );.... // The value of the slave producer can be stopped at any time. End ;
Finally Free; End ;
End ;

At this time, some smart people will turn their eyes to the asymmetric (asypolicric-coroutine) coroutine. It is not hard to see that the iterator here is the producer (data provider, which is also called generator) in coroutine ).
Once the user (Consumer consumer role) calls iterator. Next (coroutine. Resume ),
The iterator will continue to execute the following step, and then place the node of the internal data that is currently encountered into a public buffer that the consumer user can see (for example, directly put the local variable in the consumer thread stack) and then stop (coroutine. yield ). Then the consumer obtains the node from the buffer. In this way, the iterator can perform recursive operations on its own without having to manage the stack context, but the coroutine mechanism helps it allocate and manage the running stack. In this way, the "control" and "behavior" can be completely decoupled. See the following C # program:

  Using  System. Collections. Generic;
Public Class Myproducer: ienumerable < String > { // Iterator block to implement enumeration element control Public Ienumerator < String > Getenumerator ()
{
For ( Int I = 0 ; I < Elements. length; I ++ )
Yield Elements [I];
}
...
}
Foreach ( String Item In New Myproducer ())
{
// Print elements on the terminal Console. writeline (item );
}

During this code execution, the foreach's loop body and getenumerator
The function body is actually executed alternately in the same thread. This is a collaborative execution mode between threads and sequential execution. It is called coroutine because scheduling between multiple code blocks executed simultaneously is completed by logical implicit collaboration. In terms of collaborative execution, the function can be divided into two parts: behavior and control. The control can be further divided into control logic and control status. The action corresponds to how to process the target object. In the code above, the action is to print the target object to the terminal, and the control is to traverse this
Elements
Array, which can be further subdivided into control logic (sequential traversal) and control status (the element to which the current traversal belongs ). Its central idea is to completely separate its behavior and control through coroutine program control mechanism and yield, so as to further reduce the code coupling, enhance universality, and improve the code reuse rate.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.