>
> I am enjoying this.  The real reason I wrote the original post was to get 
> some help in understanding processes, thread and servers.
>
> I'll interrupt my study to ask this rhetorical question. Why do I think this 
> hilarious cartoon <https://abstrusegoose.com/474> applies to Leo's 
> documentation? 
>
> Ha, ha, ha, that cartoon is really great. 

I don't have much time to write right now, but here is a list of (IMHO) 
essential facts one should keep in mind when dealing with concurrency:

   - in terms of required computer resources most expensive are processes, 
   then threads, and then micro-threads (where by micro-threads I understand 
   both doing several tasks simultaneously on different CPU cores and some 
   clever concepts implemented in frameworks like Actors in Akka, or something 
   similar in Twisted). It is not so cheap to spawn 10 processes, or 100 
   threads. But you can easily run 10000 Actors on modest hardware. 
   - One should not communicate by sharing state between concurrent 
   threads/processes. Instead it is much better to share the state by 
   communicating (like you used queue in Leo debugger)
   - Pure functions (functions that do not produce any side effect) are 
   best friends when dealing with concurrency. These functions return the same 
   result for the same input arguments no matter when and in which order they 
   are called. That helps by letting programmer free to call them in any order 
   and from any thread, or to call them even more than once if necessary. For 
   example in Clojure and ClojureScript there is a kind of variable called 
   atom. Atoms contain some data that can be safely read at any time from any 
   thread. If one want to change the data contained in atom, it isn't possible 
   to write new data directly to atom. Instead one should provide a pure 
   function and perhaps some additional arguments to it. This function will be 
   called with the current data from atom and the rest arguments and its 
   result will be written in atom. If two or more threads try to change atom 
   at the same time, then right before writing result to atom,  atom will 
   check if it currently holds the same data that was passed as an argument to 
   pure function. In case it was changed sometimes between the call and the 
   returning result (by some other thread), then its result is dropped and 
   function is called again using fresh data. This is done automatically by 
   system, so programmer doesn't have to think about it all the time. The only 
   thing programmer must obey is that function must be pure and it may be 
   called more than once.
   - server usually try to serve more than one client simultaneously, it 
   can't be sure at what time client will send request. Clients often don't 
   know about each other. So servers are usually implemented using some kind 
   of concurrency. Sometimes they use several processes, sometime they use 
   several threads, and sometimes they use both several processes and several 
   threads. Most common thing is to dedicate one thread for each incoming 
   request. However that limits how many simultaneous requests can server 
   accept. If you wish to allow more requests then it is usually achieved by 
   using small number of threads from the pool of threads and using some kind 
   of micro-threads like Promises, Actors, ... 
   - Server and client may be on different machines so they usually can't 
   share state. To have same data on the server and in the client they need to 
   encode data in some format acceptable for both parties and to agree on some 
   kind of protocol how and in which order will they exchange encoded 
   messages. Upon receiving encoded messages both client and server decode 
   them and use data to adjust their own internal state to match the state of 
   the other side (server/client, client/server). Even if both server and 
   client are on the same machine, they may be in different processes.
   - Its always easier if you have same language on both sides server and 
   client, but this is not required and very often is not the case. OTOH it 
   allows you to completely replace one of them with totally different 
   implementation and only to keep same protocol and same data encoding and 
   other side won't notice the difference. For example if we have Leo server 
   implemented in Python, and client implemented in JavaScript, then it would 
   be possible (not necessarily easy) to re-implement Leo server in some other 
   language (say Clojure for example), and its JavaScript won't know. Or it 
   would be possible to implement client using python and some other gui 
   library, and server will work without change. 
   - Go language has very nice way to make spawning new tasks to several 
   CPU cores on micro level and re-synchronizing them very cheaply and very 
   easily. Rust prevents sharing mutable state, code won't compile if you try 
   to share mutable variable. Clojure and ClojureScript allow you to work with 
   concurrency in the same way Go language does. The macro for starting async 
   operation is named 'go'.
   - Actors in Akka (and I believe something similar is in Twisted) have 
   own mailbox each for accepting messages. You can't have a reference to an 
   Actor object. Only thing you can have is its "postal" address. So you can 
   send a message to an actor, but you can't be sure if the actor will ever 
   receive it  or not. Actor may be dead by the time message arrives or it can 
   move to some other address. The only way to be sure is if the actor replies 
   by some message confirming that your previous message is accepted. But the 
   return message may be lost as well, so you can't be really sure. But you 
   can rely on the fact that if you send to one actor three messages (A, B, C) 
   and you send them in that order, they will be eventually received in 
   exactly the same order. Actor may receive some other messages from other 
   actors in between these three messages but one can be sure that from one 
   actor to another messages are comming in the same order they were sent (if 
   they ever come).
   - Promises, Actors, micro-threads, compiler ensuring mutable state is 
   never shared, are all invented to allow programmer to rely on something in 
   order to simplify writing, reading, understanding and reasoning about 
   concurrent programs. If you cant rely on anything, then you cant possibly 
   understand what program will do.

I've got to go now. 
HTH
Vitalije

>

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to leo-editor+unsubscr...@googlegroups.com.
To post to this group, send email to leo-editor@googlegroups.com.
Visit this group at https://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.

Reply via email to