AsyncIO Micropython: metode sinkronisasi dalam pemrograman asinkron

Dalam pemrograman berurutan, saya terus-menerus menghadapi keinginan yang jelas untuk tidak menghentikan program pada saat tujuan tugas tertentu (proses) adalah tindakan berkala - misalnya, menghitung nilai sensor, atau mentransmisikan data sesuai jadwal ke server, atau input / output sejumlah besar data. Hal yang paling sederhana, tentu saja, adalah menunggu selesainya acara berkala dan kemudian, perlahan, terus melakukan tugas-tugas lain.

while True: do_ext_proc_before() do_internal_proc() sleep(5) do_ext_proc_after() 

Anda dapat mengabaikan 'sleep ()' dan mengaktifkan pengecekan untuk beberapa kondisi dalam loop, yang memungkinkan Anda untuk tidak menunda loop utama setidaknya sampai peristiwa periodik terjadi:

  start = time() set_timer(start,wait=5) #   set_timeout(start,wait_to=7) #   set_irq(alarm) #    while True: curTime = time() do_ext_proc_before() if timer(curTime) or timeout(curTime) or alarm: # if all events crazy start simultaneously - reset all start = time() set_timer(start,wait=5) #   set_timeout(start,wait_to=7) #   set_irq(alarm) #    do_internal_proc() do_ext_proc_after() 

Dalam pemrograman asinkron, setiap tugas menjadi proses independen dan dieksekusi, tergantung pada implementasi spesifik, secara paralel atau pseudo-paralel, menggunakan pemahaman internal tentang kondisi menunggu yang dibuat secara alami atau buatan atau penggunaan sumber daya terbatas, misalnya, disk atau saluran komunikasi.

  setTask(do_ext_proc_before()) setTask(do_internal_proc(),timer=5,timeout=7,alarm_handler=alarm) setTask(do_ext_proc_after()) runTasks() 

Sekarang muncul masalah yang tidak ada dalam pemrograman berurutan - apa yang harus dilakukan jika perlu menyinkronkan beberapa proses dengan asinkronnya
lakukan? Misalnya, setelah menerima data dari sensor, memulai proses pengiriman data ke server atau merespons keadaan darurat. Selain itu, dalam pemrograman asinkron, organisasi input / output asinkron secara organik diselesaikan dalam standar bahasa, dan situasi lain diselesaikan di perpustakaan.

Saya mempelajari pertanyaan ini menggunakan asyncio Micropython extended library yang diterbitkan
Peter Hinch ( https://github.com/peterhinch/micropython-async/blob/master/TUTORIAL.md )
Solusi paling sederhana adalah memberi sinyal acara ke proses yang tertarik. Untuk melakukan ini, gunakan kelas Event (), yang berisi beberapa modul

  Event.Set( timeout = None, data = None ) -    (Event = True), ,     , Event.IsSet() - ,   ,  True,    False   Event.Wait() -   ,     - Done,Timeout,Cancel Event.Data() -  ,     Event.Clear() -   (Event = False). 

Penyelesaian dicatat, sebagai suatu peraturan, oleh proses yang menunggu peristiwa terjadi, misalnya, proses menampilkan di layar atau proses menyimpan data ke disk, atau batas waktu, maka tidak perlu memperbarui atau menyimpan data, karena mereka tidak diperbarui karena alasan apa pun, atau karena gangguan pada kejadian peristiwa penting lainnya, misalnya, transisi ke mode tidur atau reboot, yang mungkin memerlukan pelepasan semua proses yang tertunda dengan mengatur ulang acara yang sesuai.

Harus diingat bahwa disarankan untuk membuat Event.Clear () dengan hanya satu proses, jika ini tidak bertentangan dengan algoritma yang diberikan. Jika tidak, jika beberapa proses sedang menunggu acara Event.Set () terjadi, diasumsikan bahwa Event.Clear () harus dilakukan oleh salah satu proses yang tertarik, hanya memastikan bahwa semua proses yang tertarik telah merespons acara tersebut. Ini menyulitkan logika keputusan ketika menggunakan Event-Class ketika menunggu suatu peristiwa dengan beberapa proses. Situasi ini diselesaikan dengan menetapkan sejumlah Clear () dari peristiwa yang terjadi.

  Barrier.Set( quantity = 1, timeout = None, data = None ) - quantity = 1  Event.Set() Barrier.IsSet() - ,   ,  True,    False   Barrier.Wait() -   ,     - Done,Timeout,Cancel Barrier.Data() -  ,     Barrier.qty -      Barrier.Clear() -   (Event = False),        Barrier.quantity  ,    ,     

Pada saat yang sama, tidak ada akuntansi yang disimpan - proses spesifik mana yang telah merespons, dan yang belum, yang dapat menimbulkan masalah reaksi berulang terhadap peristiwa tersebut, jika ini penting untuk algoritma yang diberikan. Jika alih-alih Barrier.quantity Anda melewati daftar nama proses yang diminati, konflik ini dapat dihindari. Juga, dalam hal timeout atau gangguan acara, Anda dapat menentukan proses tertunda tertentu yang belum berfungsi. Semua hal di atas berlaku untuk situasi di mana satu atau lebih proses menunggu terjadinya peristiwa tertentu, atau situasi satu-ke-banyak. Ini terjadi ketika proses atau proses do_ext_proc_after () selama pemrograman berurutan akan dieksekusi hanya setelah penyelesaian do_internal_proc (). Untuk kenyamanan pemahaman lebih lanjut, kami akan memperluas Kelas Acara dan Kelas Penghalang ke dalam Kelas EEvent baru dan menjadikannya atau objek yang dihasilkannya - global. Di sini 'pencipta' adalah nama atau daftar nama proses yang memicu acara atau membuka sumber daya, 'pengikut' adalah nama atau daftar nama proses yang menunggu acara atau membuka kunci sumber daya

  EEvent.Set (creators, folowers, timeout = None, data = None ) -  True,        EEvent.IsSet( procName ) - procName -   ID   EEvent.Wait( procName ) EEvent.Clear( procName ) EEvent.Folowers() -    ,      . Barrier.qty = len(EEvent.List()) EEvent.Creators() -   ,     

Dengan menggunakan modul EEvent-Class, kita dapat menggambarkan solusi untuk masalah yang telah dibahas sebelumnya.

  def do_internal_proc(): ... EEvent.Set ('p_Creator',('p_Folwer1','p_Folwer2')) # exec 'p_Folwer1','p_Folwer2' after event is come in 'p_Creator' ... def do_ext_proc_after1() ... EEvent.Wait('p_Creator') ... EEvent.Clear('p_Folwer1') def do_ext_proc_after1() ... EEvent.Wait('p_Creator') ... EEvent.Clear('p_Folwer2') 

Pertimbangkan situasi yang berlawanan - ketika satu proses sedang menunggu penyelesaian beberapa peristiwa, atau situasi 'banyak ke satu'. Dengan kata lain, bahwa jika eksekusi do_internal_proc () dapat dilakukan hanya setelah eksekusi do_ext_proc_before (). Dalam kasus ekstrem, ketika satu proses menunggu penyelesaian / terjadinya satu peristiwa, tugas dapat diselesaikan menggunakan kelas-Acara. Ketika penyelesaian beberapa peristiwa diharapkan, misalnya, hanya setelah menampilkan data yang diterima dan mengirimkannya ke server, menyimpannya ke disk, perlu bahwa setiap proses yang dieksekusi menetapkan partisipasinya dalam acara yang diharapkan dan menunggu sampai semua proses selesai.

  def do_ext_proc_before1() ... EEvent.Set('p_Creator1','p_Folwer') ... def do_ext_proc_before2() ... EEvent.Set('p_Creator2','p_Folwer') ... def do_internal_proc(): ... EEvent.Wait(('p_Creator1','p_Creator2')) ... EEvent.Clear('p_Folwer') 

Aspek penting lain dari pemrograman asinkron adalah berbagi sumber daya yang terbatas. Misalnya, pemutakhiran data harus dilakukan hanya oleh satu proses, sisa proses yang mengklaim tindakan serupa harus mengantri atau menunggu hingga data diperbarui. Pada saat yang sama, ada kemungkinan bahwa membaca data untuk tampilan atau penerusan tidak akan menjadi kritis. Oleh karena itu, perlu mengetahui daftar proses yang bersaing ketika mengatur acara yang relevan.

Dalam standar pemrograman asinkron, tugas ini diselesaikan oleh modul Lock-Class. Dalam kasus umum, masalahnya juga dapat diselesaikan dengan cara yang sama dengan situasi satu-ke-banyak.

  def do_internal_proc(): # lock activity all 'followers' in list ... EEvent.Set ('p_Creator',('p_Folwer1','p_Folwer2')) # exec 'p_Folwer1','p_Folwer2' after event is come in 'p_Creator' ... def do_ext_proc_after1() ... EEvent.Wait('p_Creator') # waiting for recourse releale if ( EEvent.Set ('p_Folwer1','p_Folwer2')): # lock resource 'p_Folower1' now is 'p_Creator' ... else: EEvent.Wait('p_Folower2') # continue waiting for recourse releale ... EEvent.Clear('p_Folwer1') # releafe recourse def do_ext_proc_after1() ... EEvent.Wait('p_Creator') if ( EEvent.Set ('p_Folwer2','p_Folwer1')): # lock resource 'p_Folower2' now is 'p_Creator' ... else: EEvent.Wait('p_Folower1') # continue waiting for recourse releale ... EEvent.Clear('p_Folwer2') # releafe recourse 

Selain opsi yang dipertimbangkan, ada solusi yang membatasi throughput, mengatur antrian dan penjadwalan proses yang terkontrol, tetapi dalam aktivitas saya belum ada kebutuhan untuk ini dan, sebagai hasilnya, kebutuhan untuk pemahaman yang cukup untuk diri saya sendiri, meskipun saya tidak mengecualikan bahwa ada yang lebih elegan atau keputusan ekonomis.

Sebagai kesimpulan, saya ingin mengatakan bahwa pendekatan sekuensial dan tidak sinkron memiliki hak yang sama untuk ada dan berhasil mengimplementasikan algoritma yang diberikan. Oleh karena itu, penerapan pendekatan ini atau itu ditentukan oleh prioritas pencipta - yang lebih penting baginya ketika menerapkan algoritma yang diberikan - transparansi dan keterbacaan, kecepatan atau volume kode yang dihasilkan.

Source: https://habr.com/ru/post/id442268/


All Articles