コルティンのない䞖界。ゞェネレヌタむテレヌタ

1.はじめに



問題をできるだけ混乱させるために、解決策をプログラマヌに任せおください;。しかし、真剣に、私の意芋では、コロチンにも同様のこずが起こりたす。なぜなら、喜んでかどうかにかかわらず、それらは状況を曖昧にするために䜿甚されるからです。埌者は、どこにも行かない䞊列プログラミングの問題がただあるずいう事実によっお特城付けられ、そしお最も重芁なこずに、コルチンはそれらの基本的な解決に貢献したせん。



甚語から始めたしょう。 「圌らは䜕回䞖界に語ったこずがある」が、これたでのずころ「䞖界」は非同期プログラミングず䞊列プログラミングの違いに぀いお疑問を投げかけおいる[1]の非同期のトピックに関する議論を参照。非同期性ず䞊列性を理解する問題の栞心は、䞊列性自䜓を定矩するこずから始たりたす。それは単に存圚したせん。ある皮の盎感的な理解があり、それはしばしば異なる方法で解釈されたすが、「2ず2」の操䜜の結果に぀いおの議論ほど建蚭的にすべおの質問を取り陀く科孊的定矩はありたせん。



たた、これもすべお存圚しないため、甚語ず抂念が混同されおいるため、䞊列プログラミングず同時プログラミング、非同期プログラミング、リアクティブプログラミングなどを区別しおいたす。等Felixのような機械匏蚈算機が゜フトりェア蚈算機ずは異なる動䜜をするこずに気付くのに問題が生じる可胜性は䜎いず思いたす。しかし、正匏な芳点から、すなわち䞀連の操䜜ず最終結果では、それらの間に違いはありたせん。この原則は、䞊列プログラミングの定矩で考慮に入れる必芁がありたす。



「䞍噚甚な」Felixや゜フトりェア蚈算機のような䞀貫した結果に぀ながる、䞊列凊理を蚘述するための厳密な定矩ず透過的な手段が必芁です。 「䞊列凊理」の抂念をその実装手段同じコアの数に関連付けるこずは䞍可胜です。そしお、「内郚」にあるもの-これは、䞻に「機械」の実装に埓事しおいる人だけに関心があり、そのような埓来の「䞊列蚈算機」を䜿甚しおいる人には関心がないはずです。



しかし、私たちは私たちが持っおいるものを持っおいたす。そしお、流行ではないにしおも、私たちは、コルティンず非同期プログラミングに぀いお掻発な議論をしおいたす。そしお、マルチスレッドにうんざりしおいるように芋えおも、他に䜕かが提䟛されおいない堎合は、他に䜕をすべきでしょうか。圌らはある皮の魔法に぀いおさえ話したす;しかし、あなたが理由を理解すればすべおが明らかになりたす。そしお、それらはたさにそこにありたす-䞊列凊理の面にありたす。その定矩ずその実装。



しかし、プログラミングの科孊それ以来のコンピュヌタヌのグロヌバルな、そしおある皋床は哲孊的な高さから、私たちの「眪深い地球」ぞず降りおいきたしょう。ここでは、珟圚人気のあるコトリン蚀語のメリットを損なうこずなく、Python蚀語ぞの情熱を認めたいず思いたす。おそらくい぀か、そしお他の状況では、私の奜みは倉わるでしょうが、実際、これたでのずころすべおがそうです。



これにはいく぀かの理由がありたす。その䞭には、Pythonぞの無料アクセスがありたす。これは最匷の議論ではありたせん。同じQtの䟋では、状況はい぀でも倉わる可胜性があるず述べおいたす。しかし、PythonはKotlinずは異なり、少なくずもJetBrainsず同じPyCharm環境の圢で無料ですが特に感謝したす、私の同情はその偎にありたす。たた、むンタヌネット䞊のPythonの䟋など、教育的で非垞に珟実的なロシア語の文献が倧量にあるこずも魅力的です。コトリンでは、それらはその数ではなく、それらの倚様性はそれほど倧きくありたせん。



おそらく少し先に、゜フトりェアの䞊列凊理ず非同期性の定矩ず実装の問題のコンテキストでPythonをマスタヌした結果を提瀺するこずにしたした。これは蚘事[2]によっお開始されたした..。今日は、ゞェネレヌタヌ-コルルヌチンのトピックに぀いお怜蚎したす。それらぞの私の興味は、特定の、興味深いが、珟時点ではあたり銎染みのない、珟代の蚀語/プログラミング蚀語の可胜性を認識する必芁性によっお刺激されおいたす。



私は実際には玔粋なC ++プログラマヌなので、これは倚くのこずを説明しおいたす。たずえば、Pythonのコロチンずゞェネレヌタヌが長い間存圚しおいた堎合、C ++ではただその地䜍を獲埗しおいたせん。しかし、C ++は本圓にそれを必芁ずしたすか私の意芋では、プログラミング蚀語は合理的に拡匵する必芁がありたす。 C ++は可胜な限り匕っ匵ったようで、今は急いで远い぀くようにしおいたす。しかし、同様の同時実行の問題は、coroutines / coroutinesよりも基本的な他の抂念やモデルを䜿甚しお実装できたす。そしお、この声明の背埌に蚀葉だけがないずいう事実は、さらに瀺されたす。



私たちがすべおを認めるのであれば、私はC ++に関しおかなり保守的であるこずも認めたす。もちろん、そのオブゞェクトずOOP機胜は私にずっお「私たちのすべお」ですが、私は、蚀うたでもなく、テンプレヌトに批刀的です。たあ、私は圌らの独特の「鳥の蚀語」を実際に芋たこずがありたせんでした。それは、芋たずころ、コヌドの認識ずアルゎリズムの理解を非垞に耇雑にしたす。時々私は圌らの助けに頌るこずさえしたしたが、片方の手の指でこれらすべおに十分です。私はSTLラむブラリを尊重し、それなしでは実行できたせん:)したがっお、この事実からでも、テンプレヌトに぀いお疑問を持぀こずがありたす。だから私はただできる限りそれらを避けおいたす。そしお今、私はC ++の「テンプレヌトcoroutines」を震えながら埅っおいたす;



Pythonは別の問題です。テンプレヌトが入っおいるこずにただ気づいおおらず、萜ち着きたす。しかし、その䞀方で、これは奇劙なこずに、憂慮すべきこずです。しかし、Kotlinコヌド、特にその゚ンゞンコンパヌトメントコヌドを芋るず、䞍安はすぐに消えたす;しかし、これはただ習慣ず私の偏芋の問題だず思いたす。時間が経぀に぀れお、それらを適切に認識するように自分自身を蚓緎するこずを願っおいたすテンプレヌト。



しかし...コルティンに戻りたす。珟圚、圌らはコルティンずいう名前で呌ばれおいるこずがわかりたした。名前の倉曎で䜕が新しくなりたしたかはい、実際には䜕もありたせん。前ず同じように、セットは順番に考慮されたす実行される機胜。以前ず同じように、関数を終了する前、ただし䜜業が完了する前に、戻り点が固定され、埌で䜜業が再開されたす。切り替え順序が指定されおいないため、プログラマヌ自身が独自のスケゞュヌラヌを䜜成しおこのプロセスを制埡したす。倚くの堎合、これは関数のルヌプスルヌにすぎたせん。たずえば、OlegMolchanovによるビデオのRoundRobinむベントサむクル[3]など。



これは、コルルヌチンず非同期プログラミングの最新の玹介が通垞「指で」どのように芋えるかです。このトピックに没頭するず、新しい甚語や抂念が生たれるこずは明らかです。ゞェネレヌタヌはその1぀です。さらに、それらの䟋は「䞊列蚭定」を瀺すための基瀎になりたすが、すでに私の自動解釈にありたす。



2.デヌタリストのゞェネレヌタ



だから-ゞェネレヌタ。非同期プログラミングずコロチンは、しばしばそれらに関連付けられおいたす。 Oleg Molchanovからの䞀連のビデオは、これらすべおに぀いお語っおいたす。そのため、圌はゞェネレヌタヌの重芁な機胜を「前回停止したのず同じ堎所から実行を継続するために関数の実行を䞀時停止する機胜」ず呌んでいたす詳现に぀いおは[3]を参照。そしお、これでは、すでにかなり叀いコロチンの定矩に぀いお䞊蚘で述べたこずを考えるず、新しいものは䜕もありたせん。



しかし、結局のずころ、ゞェネレヌタヌはデヌタのリストを䜜成するための非垞に特殊な甚途を芋぀けたした。このトピックの玹介は、Egorov Artem [4]のビデオですでに取り䞊げられおいたす。..。しかし、どうやら、そのようなアプリケヌションによっお、私たちは質的に異なる抂念、぀たり操䜜ずプロセスを混ぜ合わせおいたす。蚀語の蚘述機胜を拡匵するこずにより、発生する可胜性のある問題を倧郚分理解したす。ここでは、圌らが蚀うように、あたりプレむしないでください。デヌタを蚘述するためにgenerators-coroutinesを䜿甚するこずは、たさにこれに貢献しおいるように私には思えたす。 Oleg Molchanovは、ゞェネレヌタヌをデヌタ構造に関連付けるこずに察しおも譊告し、「ゞェネレヌタヌは関数である」こずを匷調しおいるこずに泚意しおください[3]。



ただし、ゞェネレヌタを䜿甚しおデヌタを定矩するこずに戻りたす。リストアむテムを蚈算するプロセスを䜜成したこずを隠すのは難しいです。したがっお、プロセスなどのリストに぀いおはすぐに疑問が生じたす。たずえば、定矩䞊、コルチンが「䞀方向」でしか機胜しない堎合、どのように再利甚するのでしょうか。プロセスにむンデックスを付けるこずができない堎合、その任意の芁玠を蚈算するにはどうすればよいですか等。等Artemはこれらの質問に答えるこずはなく、リストの芁玠ぞの繰り返しのアクセスは敎理できず、玢匕付けは受け入れられないず譊告するだけです。むンタヌネットで怜玢するず、私にも同様の質問があるだけでなく、提案されおいる解決策はそれほど簡単で明癜ではないこずがわかりたす。



もう1぀の問題は、リスト生成の速床です。これで、各coroutineスむッチでリストの単䞀芁玠を圢成したす。これにより、デヌタ生成時間が長くなりたす。 「バッチ」で芁玠を生成するこずにより、プロセスを倧幅に加速できたす。しかし、おそらくこれには問題がありたす。すでに実行䞭のプロセスを停止するにはどうすればよいですかたたは、他の䜕か。遞択したアむテムのみを䜿甚しお、リストをかなり長くするこずができたす。このような状況では、効率的なアクセスのためにデヌタの蚘憶がよく䜿甚されたす。ちなみに、Pythonに関するこのトピックに関する蚘事をすぐに芋぀けたした[5]を参照しおくださいオヌトマトンに関するメモ化の詳现に぀いおは、蚘事[6]を参照しおください。しかし、この堎合はどうですか



リストを定矩するためのそのような構文の信頌性も疑わしいかもしれたせん。括匧の代わりに角括匧を誀っお䜿甚したり、その逆を行ったりするのは簡単です。䞀芋矎しく゚レガントな゜リュヌションが実際には特定の問題に぀ながる可胜性があるこずが刀明したした。プログラミング蚀語は、技術的で柔軟性があり、䞍本意な間違いを防ぐ必芁がありたす。



ちなみに、リストずゞェネレヌタヌの長所ず短所に぀いおのトピックに぀いおは、䞊蚘の発蚀ず亀差しお、OlegMolchanovによる別のビデオを芋るこずができたす[7]。



3.ゞェネレヌタヌ-コルルヌチン



Oleg Molchanov による次のビデオ[8]は、コロチンの䜜業を調敎するためのゞェネレヌタヌの䜿甚に぀いお説明しおいたす。実際、それらはこれを目的ずしおいたす。コルチンを切り替える瞬間の遞択に泚意が向けられたす。それらの配眮は単玔なルヌルに埓いたす-ブロッキング関数の前にyieldステヌトメントを眮きたす。埌者は関数ずしお理解され、他の操䜜ず比范しお戻り時間が非垞に長いため、蚈算はそれらの停止に関連付けられたす。このため、それらはブロッカヌず呌ばれおいたした。



切り替えは、䞭断されたプロセスが、ブロッキングコヌルが埅機しないずきに正確に䜜業を続行する堎合に有効ですが、䜜業はすぐに完了したす。そしお、このために、このすべおの「隒ぎ」は、coroutine / coroutineモデルを䞭心に開始されたようであり、したがっお、非同期プログラミングの開発に匟みが぀けられたした。ただし、䞊列コンピュヌティングの仮想モデルを䜜成するずいう、coroutinesの元のアむデアはただ異なっおいたした。



怜蚎䞭のビデオでは、コロチンの䞀般的な堎合ず同様に、コルチン操䜜の継続は、むベントスケゞュヌラである倖郚環境によっお決定されたす。この堎合、event_loopずいう名前の関数で衚されたす。そしお、すべおが論理的であるように思われたす。スケゞュヌラヌは分析を実行し、必芁なずきに正確にnext挔算子を呌び出すこずによっお、ルヌチンの䜜業を続行したす。問題は、予期しない埅機にありたす。スケゞュヌラヌは非垞に耇雑になる可胜性がありたす。 Molchanovの以前のビデオ[3]を参照では、すべおが単玔でした。制埡の単玔な代替転送が実行されたしたが、ロックはありたせんでした。察応する呌び出しはありたせんでした。それでも、いずれにせよ、少なくずも単玔なスケゞュヌラヌが必芁であるこずを匷調したす。



問題1。 , next() (. event_loop). , , yield. - , , next(), .



2. , select, — . .



しかし、重芁なのはプランナヌの必芁性でさえありたせんが、圌が圌にずっお珍しい機胜を匕き受けるずいう事実です。倚くのコロチンの共同操䜜のためのアルゎリズムを実装する必芁があるずいう事実によっお、状況はさらに耇雑になりたす。Oleg Molchanovが蚀及した2぀のビデオで説明されおいるスケゞュヌラヌの比范は、同様の問題を非垞に明確に反映しおいたす。[8]の゜ケットスケゞュヌリングアルゎリズムは、[3]の「カルヌセル」アルゎリズムよりも著しく耇雑です。



3.コルティンのない䞖界ぞ



オヌトマトンで察抗する、コルティンのない䞖界が可胜であるず確信しおいるので、同様のタスクがすでにそれらによっおどのように解決されおいるかを瀺す必芁がありたす。゜ケットを操䜜する同じ䟋を䜿甚しお、これを瀺したしょう。その最初の実装は、すぐに理解できるほど簡単ではないこずが刀明したこずに泚意しおください。これは、ビデオの䜜者自身によっお繰り返し匷調されおいたす。他の人は、コルティンの文脈で同様の問題に盎面しおいたす。したがっお、認識、理解、デバッグなどの耇雑さに関連するコロチンの欠点。ビデオ[10]で説明されおいたす。



たず、怜蚎䞭のアルゎリズムの耇雑さに぀いお簡単に説明したす。これは、カスタマヌサヌビスプロセスの動的で耇数の性質によるものです。これを行うために、特定のポヌトをリッスンするサヌバヌが䜜成され、芁求が衚瀺されるず、それに到達する倚くのクラむアントサヌビス関数が生成されたす。倚くのクラむアントが存圚する可胜性があるため、それらは予期せず衚瀺されたす。動的リストは、゜ケットにサヌビスを提䟛し、それらず情報を亀換するプロセスから䜜成されたす。ビデオ[8]で説明されおいるPythonゞェネレヌタ゜リュヌションのコヌドをリスト1に瀺したす。



リスト1.ゞェネレヌタヌの゜ケット
import socket
from select import select
tasks = []
to_read = {}
to_write = {}

def server():

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('localhost', 5001))
    server_socket.listen()

    while True:
        yield ('read', server_socket)
        client_socket, addr = server_socket.accept()    
        print('Connection from', addr)
        tasks.append(client(client_socket, addr))       
    print('exit server')

def client(client_socket, addr):

    while True:
        yield ('read', client_socket)
        request = client_socket.recv(4096)              

        if not request:
            break
        else:
            response = 'Hello World\n'.encode()

            yield ('write', client_socket)

            client_socket.send(response)                
    client_socket.close()                               
    print('Stop client', addr)

def event_loop():
    while any([tasks, to_read, to_write]):

        while not tasks:

            ready_to_read, ready_to_write, _ = select(to_read, to_write, [])

            for sock in ready_to_read:
                tasks.append(to_read.pop(sock))

            for sock in ready_to_write:
                tasks.append(to_write.pop(sock))
        try:
            task = tasks.pop(0)

            reason, sock = next(task)   

            if reason == 'read':
                to_read[sock] = task
            if reason == 'write':
                to_write[sock] = task
        except StopIteration:
            print('Done!')
tasks.append(server())
event_loop()




サヌバヌずクラむアントのアルゎリズムはかなり基本的です。しかし、サヌバヌがクラむアント機胜をタスクリストに远加するこずは憂慮すべきこずです。さらに-詳现event_loopむベントルヌプのアルゎリズムを理解するこずは困難です。少なくずもサヌバヌプロセスが垞に存圚する必芁がある堎合、タスクリストが空になるたでは..



次に、蟞曞to_readずto_writeが導入されたす。蟞曞を扱う䜜業そのものには、別の説明が必芁です。通垞のリストで䜜業するよりも難しいです。このため、yieldステヌトメントによっお返される情報はそれらに合わせお調敎されたす。するず、蟞曞の呚りで「タンバリンず螊る」が始たり、すべおが䞀皮の「シヌズ」のようになりたす。䜕かが蟞曞に配眮されおいるように芋え、そこからタスクリストに入るなどです。等あなたは「頭を壊す」こずができ、これらすべおを敎理したす。



そしお、目前のタスクの解決策はどのようになりたすかオヌトマトンが、ビデオですでに説明した゜ケットを操䜜するプロセスず同等のモデルを䜜成するこずは論理的です。サヌバヌモデルでは、䜕も倉曎する必芁がないように芋えたす。これは、server関数のように機胜するオヌトマトンになりたす。そのグラフを図1に瀺したす。 1a。オヌトマトンアクションy1は、サヌバヌ゜ケットを䜜成し、指定されたポヌトに接続したす。述語x1はクラむアント接続を定矩し、存圚する堎合、y2アクションはクラむアント゜ケットサヌビスプロセスを䜜成し、埌者をアクティブオブゞェクトクラスを含むクラスプロセスリストに配眮したす。



図では図は、個々のクラむアントのモデルのグラフを瀺しおいる。オヌトマトンは状態「0」にあるため、クラむアントが情報を送信する準備ができおいるかどうかを刀断し述語x1-true、状態「1」ぞの遷移時にアクションy1内で応答を受信したす。さらに、クラむアントが情報を受信する準備ができおいる堎合x2はすでにtrueである必芁がありたす、アクションy2は、初期状態「0」ぞの遷移時にクラむアントにメッセヌゞを送信する操䜜を実装したす。クラむアントがサヌバヌずの接続を切断するずこの堎合、x3はfalse、オヌトマトンは状態「4」に切り替わり、y3アクションでクラむアント゜ケットを閉じたす。プロセスは、アクティブなクラスのリストから陀倖されるたで「4」状態のたたですリストの圢成に぀いおは、サヌバヌモデルの䞊蚘の説明を参照しおください。



図では1cは、リスト1のevent_loop関数ず同様のプロセスの起動を実装するオヌトマトンを瀺しおいたす。この堎合のみ、その操䜜アルゎリズムははるかに単玔です。それはすべお、マシンがアクティブなクラスのリストの芁玠を調べお、それらのそれぞれに察しおloopメ゜ッドを呌び出すずいう事実に垰着したす。このアクションはy2によっお実装されたす。 y4アクションは、状態「4」にあるクラスをリストから陀倖したす。残りのアクションは、オブゞェクトのリストのむンデックスで機胜したす。y3アクションはむンデックスを増やし、y1アクションはむンデックスをリセットしたす。



Pythonのオブゞェクトプログラミング機胜は、C ++のオブゞェクトプログラミングずは異なりたす。したがっお、オヌトマトンモデルの最も単玔な実装が基瀎ずしお䜿甚されたす正確には、オヌトマトンの暡倣です。これは、プロセスを衚すずいうオブゞェクトの原則に基づいおおり、各プロセスは個別のアクティブクラスに察応したす゚ヌゞェントずも呌ばれたす。このクラスには、必芁なプロパティずメ゜ッドが含たれ特定のオヌトマトンメ゜ッドの詳现- [9]の述語ずアクションを参照、オヌトマトン操䜜のロゞック遷移関数ず終了関数は、loopず呌ばれるメ゜ッドのフレヌムワヌク内に集䞭しおいたす。オヌトマトンの動䜜のロゞックを実装するために、if-elif-else構造を䜿甚したす。



このアプロヌチでは、「むベントルヌプ」は゜ケットの可甚性の分析ずは䜕の関係もありたせん。それらは、述語内で同じselectステヌトメントを䜿甚するプロセス自䜓によっおチェックされたす。この状況では、それらはリストではなく単䞀の゜ケットで動䜜し、この特定の゜ケットに期埅される動䜜をチェックし、正確には動䜜アルゎリズムによっお決定される状況で動䜜したす。ちなみに、そのような実装をデバッグする過皋で、selectステヌトメントの予期せぬブロッキング゚ッセンスが珟れたした。



図1.゜ケットを操䜜するための自動プロセスのグラフ
image



リスト2は、゜ケットを操䜜するためのPythonのオヌトマトンオブゞェクトコヌドを瀺しおいたす。これは、私たちの䞀皮の「コロチンのない䞖界」です。これは、゜フトりェアプロセスを蚭蚈するためのさたざたな原則を持぀「䞖界」です。これは、䞊列蚈算のアルゎリズムモデルの存圚を特城ずしおいたす詳现に぀いおは、[9]を参照しおください。これは、オヌトマトンプログラミングテクノロゞAPず「通垞のテクノロゞ」の䞻な定性的な違いです。



Automatonプログラミングは、プログラム蚭蚈、プロセスの䞊列凊理、および同時にプログラマヌの心が考えるこずができるすべおの非同期原則を簡単に実装したす。私の以前の蚘事では、自動蚈算の構造モデルの説明ずそのアプリケヌションの䟋ぞの正匏な定矩から始めお、これをより詳现に説明しおいたす。䞊蚘のPythonのコヌドは、コルヌチンのコルヌチン原則の自動実装を瀺しおおり、それらを完党にオヌバヌラップさせ、ステヌトマシンモデルで補足および拡匵しおいたす。



リスト2.マシン䞊の゜ケット
import socket
from select import select

timeout = 0.0; classes = []

class Server:
    def __init__(self): self.nState = 0;

    def x1(self):
        self.ready_client, _, _ = select([self.server_socket], [self.server_socket], [], timeout)
        return self.ready_client

    def y1(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.bind(('localhost', 5001))
        self.server_socket.listen()
    def y2(self):
        self.client_socket, self.addr = self.server_socket.accept()
        print('Connection from', self.addr)
        classes.append(Client(self.client_socket, self.addr))

    def loop(self):
        if (self.nState == 0):      self.y1();      self.nState = 1
        elif (self.nState == 1):
            if (self.x1()):         self.y2();      self.nState = 0

class Client:
    def __init__(self, soc, adr): self.client_socket = soc; self.addr = adr; self.nState = 0

    def x1(self):
        self.ready_client, _, _ = select([self.client_socket], [], [], timeout)
        return self.ready_client
    def x2(self):
        _, self.write_client, _ = select([], [self.client_socket], [], timeout)
        return self.write_client
    def x3(self): return self.request

    def y1(self): self.request = self.client_socket.recv(4096);
    def y2(self): self.response = 'Hello World\n'.encode(); self.client_socket.send(self.response)
    def y3(self): self.client_socket.close(); print('close Client', self.addr)

    def loop(self):
        if (self.nState == 0):
            if (self.x1()):                     self.y1(); self.nState = 1
        elif (self.nState == 1):
            if (not self.x3()):                 self.y3(); self.nState = 4
            elif (self.x2() and self.x3()):     self.y2(); self.nState = 0

class EventLoop:
    def __init__(self): self.nState = 0; self.i = 0

    def x1(self): return self.i < len(classes)

    def y1(self): self.i = 0
    def y2(self): classes[self.i].loop()
    def y3(self): self.i += 1
    def y4(self):
        if (classes[self.i].nState == 4):
            classes.pop(self.i)
            self.i -= self.i

    def loop(self):
        if (self.nState == 0):
            if (not self.x1()): self.y1();
            if (self.x1()):     self.y2(); self.y4(); self.y3();

namSrv = Server(); namEv = EventLoop()
while True:
    namSrv.loop(); namEv.loop()




リスト2のコヌドは、リスト1のコヌドよりもはるかに技術的に進んでいたす。これが、自動蚈算モデルのメリットです。これは、オヌトマトンの動䜜をプログラミングオブゞェクトモデルに統合するこずで容易になりたす。その結果、オヌトマトンプロセスの動䜜のロゞックは、生成された堎所に正確に集䞭し、通垞のようにプロセス制埡のむベントルヌプに委任されるこずはありたせん。新しい゜リュヌションは、ナニバヌサルな「むベントルヌプ」の䜜成を匕き起こしたす。そのプロトタむプは、EventLoopクラスのコヌドず芋なすこずができたす。



4.SRPおよびDRYの原則に぀いお



「単䞀責任」の原則-SRP単䞀責任原則および「自分自身を繰り返さない」-DRY自分自身を繰り返さないは、OlegMolchanovによる別のビデオのコンテキストで衚明されおいたす[11]。圌らによるず、関数には、SRYの原則に違反しないようにタヌゲットコヌドのみが含たれ、DRYの原則に違反しないように「远加コヌド」の繰り返しに寄䞎しないようにする必芁がありたす。この目的のために、デコレヌタを䜿甚するこずが提案されおいたす。しかし、別の解決策がありたす-自動です。



前回の蚘事[2]そのような原則の存圚に気づかずに、デコレヌタを䜿甚しお䟋が瀺されたした。ちなみに、必芁に応じおリストを生成できるカりンタヌを怜蚎したした。カりンタヌの皌働時間を枬定するストップりォッチオブゞェクトに぀いお説明したす。オブゞェクトがSRPおよびDRYの原則に準拠しおいる堎合、その機胜は通信プロトコルほど重芁ではありたせん。実装では、カりンタヌコヌドはストップりォッチコヌドずは関係がなく、オブゞェクトのいずれかを倉曎しおも他のオブゞェクトには圱響したせん。それらはプロトコルによっおのみ拘束され、オブゞェクトは「海岞で」同意し、厳密にそれに埓いたす。



したがっお、䞊列オヌトマトンモデルは、基本的にデコレヌタの機胜を䞊曞きしたす。それらの機胜を実装する方がより柔軟で簡単です。機胜コヌドを「囲み」たせん装食したせん。オヌトマトンず埓来の技術の客芳的な評䟡ず比范を目的ずしお、リスト3は、前の蚘事[2]で説明したカりンタヌのオブゞェクトアナログを瀺しおいたす。ここでは、実行時刻を含む簡略化されたバヌゞョンず、カりンタヌの元のバヌゞョンがコメントの埌に瀺されおいたす。



リスト3.自動カりンタヌの実装
import time
# 1) 110.66 sec
class PCount:
    def __init__(self, cnt ): self.n = cnt; self.nState = 0
    def x1(self): return self.n > 0
    def y1(self): self.n -=1
    def loop(self):
        if (self.nState == 0 and self.x1()):
            self.y1();
        elif (self.nState == 0 and not self.x1()):  self.nState = 4;

class PTimer:
    def __init__(self, p_count):
        self.st_time = time.time(); self.nState = 0; self.p_count = p_count
#    def x1(self): return self.p_count.nStat == 4 or self.p_count.nState == 4
    def x1(self): return self.p_count.nState == 4
    def y1(self):
        t = time.time() - self.st_time
        print ("speed CPU------%s---" % t)
    def loop(self):
       if (self.nState == 0 and self.x1()): self.y1(); self.nState = 1
       elif (self.nState == 1): pass

cnt1 = PCount(1000000)
cnt2 = PCount(10000)
tmr1 = PTimer(cnt1)
tmr2 = PTimer(cnt2)
# event loop
while True:
    cnt1.loop(); tmr1.loop()
    cnt2.loop(); tmr2.loop()

# # 2) 73.38 sec
# class PCount:
#     def __init__(self, cnt ): self.n = cnt; self.nState = 0
#     def loop(self):
#         if (self.nState == 0 and self.n > 0): self.n -= 1;
#         elif (self.nState == 0 and not self.n > 0):  self.nState = 4;
# 
# class PTimer:
#     def __init__(self): self.st_time = time.time(); self.nState = 0
#     def loop(self):
#        if (self.nState == 0 and cnt.nState == 4):
#            t = time.time() - self.st_time
#            print("speed CPU------%s---" % t)
#            self.nState = 1
#        elif (self.nState == 1): exit()
# 
# cnt = PCount(100000000)
# tmr = PTimer()
# while True:
#     cnt.loop();
#     tmr.loop()

# # 3) 35.14 sec
# class PCount:
#     def __init__(self, cnt ): self.n = cnt; self.nState = 0
#     def loop(self):
#         if (self.nState == 0 and self.n > 0):
#             self.n -= 1;
#             return True
#         elif (self.nState == 0 and not self.n > 0):  return False;
#
# cnt = PCount(100000000)
# st_time = time.time()
# while cnt.loop():
#     pass
# t = time.time() - st_time
# print("speed CPU------%s---" % t)

# # 4) 30.53 sec
# class PCount:
#     def __init__(self, cnt ): self.n = cnt; self.nState = 0
#     def loop(self):
#         while self.n > 0:
#             self.n -= 1;
#             return True
#         return False
#
# cnt = PCount(100000000)
# st_time = time.time()
# while cnt.loop():
#     pass
# t = time.time() - st_time
# print("speed CPU------%s---" % t)

# # 5) 18.27 sec
# class PCount:
#     def __init__(self, cnt ): self.n = cnt; self.nState = 0
#     def loop(self):
#         while self.n > 0:
#             self.n -= 1;
#         return False
# 
# cnt = PCount(100000000)
# st_time = time.time()
# while cnt.loop():
#     pass
# t = time.time() - st_time
# print("speed CPU------%s---" % t)

# # 6) 6.96 sec
# def count(n):
#   st_time = time.time()
#   while n > 0:
#     n -= 1
#   t = time.time() - st_time
#   print("speed CPU------%s---" % t)
#   return t
#
# def TestTime(fn, n):
#   def wrapper(*args):
#     tsum=0
#     st = time.time()
#     i=1
#     while (i<=n):
#       t = fn(*args)
#       tsum +=t
#       i +=1
#     return tsum
#   return wrapper
#
# test1 = TestTime(count, 2)
# tt = test1(100000000)
# print("Total ---%s seconds ---" % tt)




さたざたなオプションの皌働時間を衚にたずめ、䜜業結果に぀いおコメントしたしょう。



  1. クラシックオヌトマトンの実装-110.66秒
  2. オヌトマトンメ゜ッドなしのオヌトマトン実装-73.38秒
  3. 自動ストップりォッチなし-35.14
  4. 各反埩で出力されおいる間のフォヌムのカりンタヌ-30.53
  5. ブロッキングサむクルのあるカりンタヌ-18.27
  6. デコレヌタ付きのオリゞナルカりンタヌ-6.96


自動カりンタヌモデルを完党に衚す最初のオプション。カりンタヌ自䜓ずストップりォッチの実行時間が最も長くなりたす。自動技術の原理をあきらめるこずで、皌働時間を短瞮するこずができたす。これに埓っお、オプション2では、述語ずアクションの呌び出しがそれらのコヌドに眮き換えられたす。このようにしお、メ゜ッド呌び出し挔算子の時間を節玄したした。これは非垞に目立ちたす。30秒以䞊、動䜜時間を短瞮したした。



3番目のオプションでもう少し節玄し、より単玔なカりンタヌ実装を䜜成したしたが、カりンタヌサむクルの各反埩で終了したした通垞の操䜜の暡倣。カりンタヌの停止をなくすこずでオプション5参照、カりンタヌの䜜業を倧幅に削枛したした。しかし同時に、私たちは通垞の仕事の利点を倱いたした。オプション6-これは、デコレヌタがすでに繰り返されおいる元のカりンタヌであり、実行時間が最小です。ただし、オプション5ず同様に、これはブロッキング実装であり、関数の通垞の操䜜に぀いお説明する堎合には適しおいたせん。



5。結論



オヌトマトンテクノロゞヌを䜿甚するか、信頌できるルヌチンを䜿甚するかは、完党にプログラマヌに決定されたす。ここで私たちにずっお重芁なのは、プログラム蚭蚈には、通垞のアプロヌチずは異なるアプロヌチ/テクノロゞヌがあるこずを圌が知っおいるこずです。次の゚キゟチックなオプションを想像するこずもできたす。たず、モデル蚭蚈段階で、オヌトマトン゜リュヌションモデルが䜜成されたす。それは厳密に科孊的で、蚌拠に基づいおおり、十分に文曞化されおいたす。次に、たずえば、パフォヌマンスを向䞊させるために、リスト3に瀺すように、コヌドの「通垞の」バヌゞョンに「倉圢」されたす。コヌドの「逆リファクタリング」を想像するこずもできたす。 7番目のオプションから1番目のオプションぞの移行ですが、これは可胜ですが、むベントの可胜性が最も䜎いコヌスです:)



図。 2は、トピック「非同期」に関するビデオのスラむドを瀺しおいたす[10]。..。そしお、「悪い」は「良い」を䞊回っおいるようです。そしお、私の意芋では、オヌトマトンが垞に優れおいる堎合、非同期プログラミングの堎合は、圌らが蚀うように、あなたの奜みに合わせお遞択しおください。しかし、「悪い」オプションが最も可胜性が高いようです。たた、プログラマヌは、プログラムを蚭蚈するずきに、これに぀いお事前に知っおおく必芁がありたす。



図2.非同期プログラミングの特城
image



確かに、オヌトマトンコヌドはやや「眪がないわけではない」。コヌドの量が少し倚くなりたす。しかし、最初に、それはよりよく構造化されおいるので、理解しやすく、維持しやすいです。そしお、第二に、それは垞に倧きくなるずは限りたせん。耇雑さが増すに぀れ、たずえば、オヌトマトンメ゜ッドの再利甚によりさらに利益が埗られる可胜性がありたす。デバッグがより簡単で明確になりたす。はい、結局のずころ、それは完党にSRPずDRYです。そしお、これは時々、はるかに重芁です。



たずえば、関数の蚭蚈の暙準に泚意を払うこずが望たしく、おそらく必芁ですらありたす。プログラマヌは、可胜な限り、ブロッキング関数の蚭蚈を避ける必芁がありたす。これを行うには、蚈算プロセスを開始するだけで、完党性がチェックされるか、䟋で怜蚎されおいるselect関数のように開始の準備ができおいるかどうかをチェックする手段が必芁です。リスト4に瀺すように、DOS時代にさかのがる関数を䜿甚するコヌドは、そのような問題には長い「ルヌチン前」の履歎があるこずを瀺しおいたす。



リスト4.キヌボヌドから文字を読み取る
/*
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int C=0;
    while (C != 'e')
    {
        C = getch();
        putchar (C);
    }
    return a.exec();
}
*/
//*
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int C=0;
    while (C != 'e')
    {
        if (kbhit()) {
            C = getch();
            putch(C);
        }
    }

    return a.exec();
}
//*/




キヌボヌドから文字を読み取るための2぀のオプションがありたす。最初のオプションはブロッキングです。蚈算をブロックし、getch関数がキヌボヌドから文字を受け取るたで、ステヌトメントを実行しお文字を出力したせん。 2番目のバリアントでは、ペアの関数kbhitが文字が入力バッファヌにあるこずを確認したずきに、同じ関数が適切なタむミングでのみ起動されたす。したがっお、蚈算がブロックされるこずはありたせん。



関数自䜓が「重い」堎合、぀たり䜜業にはかなりの時間が必芁であり、コルチンの䜜業の皮類によっお定期的に終了するこれは、同じコルチンのメカニズムを䜿甚せずに実行できるため、バむンドされないようにするこずは困難であるか、あたり意味がありたせん。その堎合、そのような機胜を別のスレッドに配眮しおから、䜜業の完了を制埡したす[2]のQCountクラスの実装を参照しおください。。



蚈算のブロックを陀倖する方法をい぀でも芋぀けるこずができたす。䞊蚘では、coroutine / coroutineメカニズムや、VKPa自動プログラミング環境などの特殊な環境を䜿甚せずに、蚀語の通垞の手段のフレヌムワヌク内で非同期コヌドを䜜成する方法を瀺したした。そしお、䜕をどのように䜿甚するかは、プログラマヌが決定したす。



文献



1.Pythonゞュニアポッドキャスト。 pythonの非同期に぀いお。 [電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = Q2r76grtNeg、無料。蚀語。ロシア治療日2020幎7月13日。

2.同時実行性ず効率性PythonずFSM。 [電子リ゜ヌス]、アクセスモヌドhabr.com/ru/post/506604、無料。ダズ。ロシア治療日2020幎7月13日。

3. Molchanov O. Pythonの非同期の基瀎4ゞェネレヌタヌずラりンドロビンむベントルヌプ。 [電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = PjZUSSkGLE8 ]、無料。蚀語。ロシア治療日2020幎7月13日。

4.48のゞェネレヌタヌずむテレヌタヌ。 Pythonのゞェネレヌタ匏。 [電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = vn6bV6BYm7w、 自由。ダズ。ロシア治療日2020幎7月13日。

5.メモ化ずカレヌPython。 [電子リ゜ヌス]、アクセスモヌドhabr.com/ru/post/335866、無料。蚀語。ロシア治療日2020幎7月13日。

6.リュブチェンコV.S.再垰ぞの察凊に぀いお。 「PCワヌルド」、No.11 / 02。www.osp.ru/pcworld/2002/11/164417

7. Molchanov O. Python Cast10-歩留たりずは䜕ですか。 [電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = ZjaVrzOkpZk、無料。蚀語。ロシア治療日2020幎7月18日。

8. Molchanov O. Pythonでの非同期の基瀎5ゞェネレヌタヌでの非同期。 [電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = hOP9bKeDOHs、無料。蚀語。ロシア治療日2020幎7月13日。

9.䞊列コンピュヌティングモデル。[電子リ゜ヌス]、アクセスモヌドhabr.com/ru/post/486622、無料。ダズ。ロシア 治療日2020幎7月20日。

10. Polishchuk A.Pythonでの非同期。[電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = lIkA0TDX8tE、無料。ダズ。ロシア 治療日2020幎7月13日。

11. Molchanov O.レッスンPythonキャスト6-デコレヌタヌ。[電子リ゜ヌス]、アクセスモヌドwww.youtube.com/watchv = Ss1M32pp5Ew、無料。蚀語。ロシア 治療日2020幎7月13日。



All Articles