Chapter9_4 non-preemptive multi-threaded

Source: Internet
Author: User
Tags assert lua

The Synergy program differs from the usual multithreading: the cooperative program is non-preemptive.

When a co-program is running, it cannot be stopped from the outside. It stops only when the co-program explicitly calls yield.

When there is no preemptive, programming becomes much easier without having to be crazy about syncing bugs.

All synchronization in the program is explicit, just make sure that a co-program calls yield outside of its critical region.

For such non-preemptive multithreading, as long as one thread invokes a blocking operation, the entire program stops before the operation is complete.

Here's an interesting way to solve this problem: download several remote files over HTTP.

The following example tests the download of the LUA source code, which uses the luasocket module:

LocalSocket =require "Socket"LocalHost ="www.lua.org"LocalFile1 ="/ftp/lua-5.3.3.tar.gz"LocalHTTP ="http/1.0\r\nuser-agent:wget/1.12 (LINUX-GNU) \r\naccept: */*\r\nhost:www.lua.org\r\nconnection:keep-alive\r\n \ r \ n"LocalSock =assert(Socket.connect (Host1, the)) Sock:send ("GET".. file1. HTTP)Repeat    LocalChunk,status,partial = Sock:receive (4096)    Print("chuck:size:",String.len(Chunkorpartial), statusor "OK")untilStatus = ="closed"sock:close ()

Under normal circumstances, the receive function returns a string. If an error occurs, nil is returned with an additional error code and the content read before the error (partial).

Next download a few files, the most stupid way is to download, but too slow. The program spends most of its time waiting for data to be received.

More specifically, the time is spent on the receive blocking call.

The workaround is that when a link has no data available, the program can read the data from other links.

It is clear that the collaboration program provides an easy way to build this concurrent download.

A new thread is created for each download task, and as long as a thread has no data available, it transfers control to a simple scheduler.

This scheduler calls other download threads.

Before rewriting the program in a synergistic program, rewrite the previous download code:

functionreceive (Connection)LocalS,status,partial = Connection:receive (2^Ten)    returnSorPartial,statusEnd functionDownload (host,file)LocalSock =assert(Socket.connect (Host, the))    LocalCount =0            --record the number of bytes receivedSock:send ("GET".. FILE: HTTP)Repeat        LocalChunk,status =receive (sock) Count= Count + #ChunkuntilStatus = ="closed"sock:close ()Print(File,count)EndDownload (host,file1)

This is a function wrapper to download a file, just call download. It takes about 18 seconds to download a file separately.

In the case of concurrency, however, the receive code cannot be blocked, so it should hang when it does not have the data available:

functionreceive (Connection) Connection:settimeout (0)--set to non-blocking    LocalS,status,partial = Connection:receive (2^Ten)    ifStatus = ="Timeout"  ThenCoroutine.yield(Connection)End    returnSorPartial,statusEnd 

The settimeout call makes the operation of this link not blocked.

Even in the case of timeouts, the connection returns what has been read, which is recorded in the partial variable.

The following code uses table threads to save all running threads for the scheduler.

The Get function guarantees that each download task is executed in a separate thread.

The scheduler itself is essentially a loop that iterates through all the threads and wakes up their execution one after the other.

When the thread is finished, it is removed from the list.

Threads = {}--table that holds active threadsfunctionget (Host,file)LocalCO =coroutine.create(function()--Create a collaboration programDownload (host,file)End)    Table.insert(Threads,co)--Insert ListEnd functionDispatch ()Locali =1     while true  Do         ifThreads[i] = =Nil  Then        --no threads.            ifthreads[1] ==Nil  Then  Break End --is the table an empty table?i =1                        --start the loop again        End        LocalStatus,res =Coroutine.resume(Threads[i])--Wake the thread to continue downloading the file        if  notRes Then             --whether the thread has completed the task            Table.remove(threads,i)--Remove the I thread from list        ElseI= i +1               --Check the next thread        End    EndEnd

Finally, the main program needs to create all the threads and invoke the scheduler.

LocalFile1 ="/ftp/lua-5.3.3.tar.gz"LocalFile2 ="/ftp/lua-5.3.2.tar.gz"LocalFile3 ="/ftp/lua-5.3.1.tar.gz"LocalFile4 ="/ftp/lua-5.3.0.tar.gz"LocalFile5 ="/ftp/lua-5.2.4.tar.gz"LocalFile6 ="/ftp/lua-5.2.3.tar.gz"LocalFile7 ="/ftp/lua-5.2.2.tar.gz"LocalFile8 ="/ftp/lua-5.2.1.tar.gz"LocalFile9 ="/ftp/lua-5.2.0.tar.gz"get (Host,file1) get (Host,file2) get (Host,file3) get (host,file4) get (host,file5) get (Host,file6) get (Host,file7) Get (Host,file8) get (Host,file9) dispatch ()--Main Loop

Downloading 9 files simultaneously takes 36 seconds and is much faster than downloading 9 files in a serial.

But it found that CPU usage ran to 98%.

To avoid this situation, you can use the Select function in Luasocket (Socket.select (RECVT, Sendt [, timeout]).

To apply this function in the current implementation, you need to fix the schedule if you are stuck in a blocking state while waiting:

functiondispatch_new ()Locali =1    LocalTimedOut = {}--RECVT Collection     while true  Do         ifThreads[i] = =Nil  Then        --no threads.            ifthreads[1] ==Nil  Then  Break End --table is empty tablei =1                        --start the loop againTimedOut = {}--walk through all threads and start a new round of traversal        End        LocalStatus,res =Coroutine.resume(Threads[i])--Wake the thread to continue downloading the file        if  notRes Then               --If the res is complete nil, only the status one return value is true. Otherwise res is the yield-passed parameter connection.             Table.remove(threads,i)--Remove the I thread from list        ElseI= i +1                 --Check the next threadtimedout[#timedout +1] =Resif#timedout = = #threads Then  --are all the threads blocked? Socket.Select(timedout)--if the thread has data, it returns            End        End    EndEnd
... ...
Dispatch_new ()--Main Loop

Receive passes the time-out connection through yield to the resume res. If all connections are timed out, the scheduler uses Select to wait for the status of these links to change.

After the final run of the modified version of the program, 9 file downloads took 24 seconds, the CPU occupancy rate of less than 5%.

Chapter9_4 non-preemptive multi-threaded

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.