Satu hal yang saya temukan berguna pada sejumlah mesin adalah stack switcher sederhana. Saya belum benar-benar menulis satu untuk PIC, tapi saya akan mengharapkan pendekatan akan berfungsi dengan baik pada PIC18 jika kedua / semua thread menggunakan total 31 atau lebih sedikit level stack. Pada 8051, rutinitas utamanya adalah:
_taskswitch:
xch a, SP
xch a, _altSP
xch a, SP
membasahi
Pada PIC, saya lupa nama stack pointer, tetapi rutinitasnya akan seperti:
_taskswitch:
movlb _altSP >> 8
movf _altSP, w, b
movff _STKPTR, altSP
movwf _STKPTR, c
kembali
Pada awal program Anda, panggil rutin task2 () yang memuat altSP dengan alamat stack alternatif (16 mungkin akan bekerja dengan baik untuk PIC18Fxx) dan menjalankan loop task2; rutinitas ini tidak boleh kembali atau hal-hal lain akan mati dengan kematian yang menyakitkan. Sebagai gantinya, ia harus memanggil _taskswitch kapan pun ia ingin memberikan kontrol ke tugas utama; tugas utama kemudian harus memanggil _taskswitch setiap kali ia ingin menyerah pada tugas sekunder. Seringkali, seseorang akan memiliki rutin kecil yang lucu seperti:
membatalkan delay_t1 (val pendek yang tidak ditandatangani)
{
melakukan
taskswitch ();
while ((unsigned short) (millisecond_clock - val)> 0xFF00);
}
Perhatikan bahwa pengalih tugas tidak memiliki sarana untuk melakukan 'menunggu kondisi' apa pun; semua yang didukungnya adalah spinwait. Di sisi lain, pengalih tugas sangat cepat sehingga percobaan pengalihan tugas () saat tugas lainnya menunggu pengakhir waktu akan beralih ke tugas lain, memeriksa pengatur waktu, dan beralih kembali lebih cepat daripada pengalih tugas pada umumnya. akan menentukan bahwa itu tidak perlu taskswitch.
Perhatikan bahwa multitasking kooperatif memiliki beberapa keterbatasan, tetapi menghindari kebutuhan untuk banyak penguncian dan kode terkait mutex lainnya dalam kasus di mana invarian yang sementara terganggu dapat dibangun kembali dengan cepat.
(Sunting): Beberapa peringatan tentang variabel otomatis dan semacamnya:
- jika sebuah rutin yang menggunakan pengalihan tugas dipanggil dari kedua utas, umumnya akan diperlukan untuk mengkompilasi dua salinan rutin (mungkin dengan # memasukkan file sumber yang sama dua kali, dengan pernyataan # definisi yang berbeda). File sumber apa pun yang diberikan akan berisi kode hanya untuk satu utas, atau yang lain akan berisi kode yang akan dikompilasi dua kali - sekali untuk setiap utas - jadi saya dapat menggunakan makro seperti "#define delay (x) delay_t1 (x)" atau #define delay (x) delay_tx (x) "tergantung pada utas mana yang saya gunakan.
- Saya percaya bahwa kompiler PIC yang tidak dapat "melihat" suatu fungsi yang dipanggil akan menganggap bahwa fungsi seperti itu dapat merusak setiap dan semua register CPU, sehingga menghindari kebutuhan untuk menyimpan register dalam rutinitas pengalihan tugas [manfaat bagus dibandingkan dengan preemptive multitasking]. Siapa pun yang mempertimbangkan pengalih tugas serupa untuk CPU lain harus mengetahui konvensi register yang digunakan. Mendorong register sebelum beralih tugas dan memunculkannya setelahnya adalah cara mudah untuk mengurus berbagai hal, dengan asumsi ruang stack yang memadai ada.
Multitasking yang kooperatif tidak memungkinkan seseorang untuk sepenuhnya keluar dari masalah penguncian dan semacamnya, tetapi hal itu benar-benar menyederhanakan banyak hal. Dalam RTOS preemptive dengan pemulung sampah, misalnya, perlu untuk memungkinkan objek untuk disematkan. Saat menggunakan switcher kooperatif, ini tidak perlu asalkan kode menganggap objek GC dapat memindahkan taskswitch kapan saja () dipanggil. Kolektor pemadatan yang tidak perlu khawatir tentang benda yang disematkan bisa jauh lebih sederhana daripada yang tidak.