Syarat : Data input-output sinkron dan asinkron. Syarat : Data input-output sinkron dan asinkron Input-output asinkron c

Syarat : Data input-output sinkron dan asinkron. Syarat : Data input-output sinkron dan asinkron Input-output asinkron c

Seorang pemrogram aplikasi tidak perlu memikirkan hal-hal seperti bagaimana program sistem bekerja dengan register perangkat. Sistem menyembunyikan detail pekerjaan tingkat rendah dengan perangkat dari aplikasi. Namun perbedaan pengorganisasian I/O dengan polling dan interupsi juga tercermin pada tataran fungsi sistem, berupa fungsi I/O sinkron dan asinkron.

Jalankan suatu fungsi I/O sinkron melibatkan memulai operasi I/O dan menunggu operasi tersebut selesai. Hanya setelah I/O selesai barulah fungsi tersebut mengembalikan kontrol ke program pemanggil.

I/O sinkron adalah cara paling familiar bagi pemrogram untuk bekerja dengan perangkat. Rutinitas input/output bahasa pemrograman standar bekerja dengan cara ini.

Memanggil suatu fungsi I/O asinkron berarti hanya memulai operasi yang sesuai. Setelah ini, fungsi tersebut segera mengembalikan kontrol ke program pemanggil tanpa menunggu operasi selesai.

Misalnya, entri data asinkron. Jelas bahwa program tidak dapat mengakses data sampai dipastikan bahwa inputnya telah selesai. Namun sangat mungkin bahwa program tersebut dapat melakukan pekerjaan lain untuk saat ini, daripada berdiam diri menunggu.

Cepat atau lambat, program masih harus mulai bekerja dengan data yang dimasukkan, tetapi pertama-tama pastikan bahwa operasi asinkron telah selesai. Untuk tujuan ini, berbagai sistem operasi menyediakan alat yang dapat dibagi menjadi tiga kelompok.

· Menunggu operasi selesai. Ini seperti “paruh kedua dari operasi sinkron.” Program pertama-tama memulai operasi, kemudian melakukan beberapa tindakan asing, dan sekarang menunggu hingga operasi selesai, seperti pada input/output sinkron.

· Memeriksa selesainya operasi. Dalam hal ini, program tidak menunggu, tetapi hanya memeriksa status operasi asinkron. Jika input/output belum selesai, maka program mempunyai kesempatan untuk berjalan beberapa waktu.

· Penugasan prosedur penyelesaian. Dalam hal ini, ketika memulai operasi asinkron, program pengguna menunjukkan kepada sistem alamat prosedur atau fungsi pengguna yang harus dipanggil oleh sistem setelah operasi selesai. Program itu sendiri mungkin tidak lagi tertarik dengan kemajuan input/output; sistem akan mengingatkannya akan hal ini saat yang tepat, menelepon fungsi yang ditentukan. Cara ini adalah yang paling fleksibel, karena pengguna dapat memberikan tindakan apa pun dalam prosedur penyelesaian.

Di Windows program aplikasi Tersedia ketiga cara untuk menyelesaikan operasi asinkron. UNIX tidak memiliki fungsi I/O asinkron, namun efek asinkron yang sama dapat dicapai dengan cara lain, dengan menjalankan proses tambahan.

I/O asinkron, dalam beberapa kasus, dapat meningkatkan kinerja dan memberikan tambahan fungsionalitas. Tanpa bentuk input asinkron yang sederhana seperti “input keyboard tanpa menunggu”, banyak permainan komputer dan simulator tidak akan mungkin dilakukan. Pada saat yang sama, logika program yang menggunakan operasi asinkron lebih kompleks dibandingkan dengan operasi sinkron.

Apa hubungan yang disebutkan di atas antara operasi sinkron/asinkron dan metode pengorganisasian input/output yang dibahas di paragraf sebelumnya? Jawab sendiri pertanyaan ini.

I/O asinkron menggunakan banyak thread

I/O yang tumpang tindih dan diperluas memungkinkan I/O dilakukan secara asinkron dalam satu thread, meskipun OS membuat threadnya sendiri untuk mendukung fungsi ini. Dalam satu atau lain bentuk, metode jenis ini sering digunakan di banyak sistem operasi awal untuk mendukung bentuk terbatas dalam melakukan operasi asinkron pada sistem thread tunggal.

Namun, Windows menyediakan dukungan multi-threading, sehingga dimungkinkan untuk mencapai efek yang sama dengan melakukan operasi I/O sinkron pada beberapa thread yang berjalan secara independen. Kemampuan ini sebelumnya telah didemonstrasikan menggunakan server multithread dan program grepMT (Bab 7). Selain itu, thread menyediakan cara yang secara konseptual konsisten dan mungkin lebih sederhana untuk melakukan operasi I/O asinkron. Alternatif metode yang digunakan dalam Program 14.1 dan 14.2 adalah memberikan setiap thread penanganan filenya sendiri, sehingga setiap thread dapat memproses setiap record keempat secara sinkron.

Cara penggunaan thread ini didemonstrasikan dalam program atouMT, yang tidak diberikan dalam buku, namun disertakan dalam materi yang diposting di situs Web. Program atouMT tidak hanya mampu berjalan di bawah kendali siapapun Versi Windows, tetapi juga lebih sederhana daripada salah satu dari dua versi program input/output asinkron, karena penghitungan penggunaan sumber daya dalam kasus ini tidak terlalu rumit. Setiap thread hanya memelihara buffernya sendiri di tumpukannya sendiri dan melakukan loop melalui serangkaian operasi baca, konversi, dan tulis yang sinkron. Pada saat yang sama, kinerja program tetap pada tingkat yang cukup tinggi.

Catatan

Program atouMT.c, yang terletak di situs Web, berisi komentar tentang beberapa kemungkinan jebakan yang mungkin menunggu Anda ketika mengizinkan beberapa thread untuk mengakses file yang sama secara bersamaan. Secara khusus, semua pegangan file individual harus dibuat menggunakan fungsi CreateHandle, bukan fungsi DuplikatHandle.

Secara pribadi, saya lebih suka menggunakan pemrosesan file multi-utas daripada I/O asinkron. Thread lebih mudah untuk diprogram dan dalam banyak kasus menyediakan lebih banyak kinerja tinggi.

Ada dua pengecualian untuk hal ini aturan umum. Yang pertama, seperti yang ditunjukkan sebelumnya dalam bab ini, berkaitan dengan situasi di mana mungkin hanya ada satu operasi yang belum diselesaikan, dan deskriptor file dapat digunakan untuk tujuan sinkronisasi. Pengecualian kedua yang lebih penting terjadi pada kasus port penyelesaian I/O asinkron, yang akan dibahas di akhir bab ini.

Dari buku Ayo Membangun Kompiler! oleh Crenshaw Jack

Dari buku Pemrograman di Prolog penulis Kloksin U.

Dari buku Bahasa Pemrograman C# 2005 dan Platform .NET 2.0. oleh Troelsen Andrew

Dari buku Panduan Administrator Basis Data Informix. penulis Kustov Viktor

Dari buku Microsoft Visual C++ dan MFC. Pemrograman untuk Windows 95 dan Windows NT pengarang Frolov Alexander Vyacheslavovich

2.2.3.2 I/O Asinkron Untuk mempercepat operasi I/O, server menggunakan paket Asynchronous I/O (AIO) miliknya sendiri atau paket Kernel Asynchronous I/O (KAIO), jika tersedia. Permintaan I/O pengguna diproses secara asinkron,

Dari buku Dasar-dasar Pemrograman Berorientasi Objek oleh Meyer Bertrand

I/O Seperti yang Anda ketahui, operator<< и >> menggeser nilai numerik ke kiri dan ke kanan sebanyak bit tertentu. Program dalam buku kami juga menggunakan pernyataan ini untuk memasukkan informasi dari keyboard dan menampilkannya di layar di sisi kiri

Dari buku Pemrograman sistem V Lingkungan Windows oleh Hart Johnson M

Input dan Output Dua kelas perpustakaan KERNEL menyediakan fasilitas input dan output dasar: FILE dan STD_FILES Di antara operasi yang didefinisikan pada objek f bertipe FILE adalah sebagai berikut: create f.make ("name") -- Mengaitkan f dengan nama file. nama.f.open_write -- Buka f untuk menulis f.open_read -- Buka f untuk

Dari buku Pemrograman di Ruby [Ideologi bahasa, teori dan praktik penerapan] oleh Fulton Hal

BAB 14 I/O Asinkron dan Port Penyelesaian Operasi I/O pada dasarnya lebih lambat dibandingkan jenis pemrosesan lainnya. Alasan perlambatan ini adalah beberapa faktor berikut: Keterlambatan karena waktu yang dihabiskan untuk mencari

Dari buku Pemrograman dalam Prolog untuk Kecerdasan Buatan penulis Bratko Ivan

10.1.7. I/O Sederhana Anda sudah familiar dengan beberapa metode I/O dari modul Kernel; kami memanggil mereka tanpa menentukan penelepon. Ini termasuk fungsi get dan put, serta print, printf, dan p (yang terakhir memanggil metode inspeksi objek untuk mencetaknya dengan cara yang dapat kita pahami).

Dari buku Bahasa Pemrograman C untuk komputer pribadi penulis Bochkov S.O.

Dari buku Pemrograman Linux dengan Contoh pengarang Robbins Arnold

Bab 6 Input dan Output Dalam bab ini, kita akan melihat beberapa fasilitas bawaan untuk menulis data dan membaca data dari file. Alat tersebut juga dapat digunakan untuk memformat objek data program untuk mendapatkan bentuk representasi eksternal yang diinginkan.

Dari buku Dasar-Dasar Pemrograman Java penulis Sukhov S.A.

Input dan Output Fungsi input dan output di pustaka C standar memungkinkan Anda membaca data dari file atau menerimanya dari perangkat input (seperti keyboard) dan menulis data ke file atau mengeluarkannya ke berbagai perangkat(misalnya, ke printer). Fungsi input/output

Dari buku QT 4: Pemrograman GUI di C++ oleh Blanchette Jasmine

4.4. Input dan Output Semua operasi I/O Linux dilakukan melalui deskriptor file. Bagian ini memperkenalkan deskriptor file, menjelaskan cara memperoleh dan membebaskannya, dan menjelaskan cara menjalankannya.

Dari buku Programmer Ideal. Bagaimana menjadi seorang profesional pengembangan perangkat lunak pengarang Martin Robert S.

Dari buku penulis

Bab 12: I/O Hampir setiap aplikasi memerlukan pembacaan atau penulisan file atau melakukan operasi I/O lainnya. Qt memberikan dukungan I/O yang sangat baik dengan QIODevice, sebuah abstraksi kuat dari "perangkat" yang dapat membaca dan menulis.

Dari buku penulis

Masukan dan Keluaran Saya juga merasa sangat penting bahwa hasil saya didorong oleh “masukan” yang tepat. Menulis kode perangkat lunak adalah pekerjaan kreatif. Kreativitas saya biasanya mencapai puncaknya ketika saya dihadapkan pada tantangan kreatif.

Seperti yang Anda ketahui, ada dua mode I/O utama: mode pertukaran dengan polling kesiapan perangkat I/O dan mode pertukaran dengan interupsi.

Dalam mode pertukaran dengan polling kesiapan, kontrol input/output dilakukan oleh prosesor pusat. Prosesor pusat mengirimkan perintah ke perangkat kontrol untuk melakukan beberapa tindakan pada perangkat input/output. Yang terakhir menjalankan perintah, menerjemahkan sinyal yang dapat dimengerti oleh perangkat pusat dan perangkat kontrol menjadi sinyal yang dapat dimengerti oleh perangkat input/output. Namun kecepatan perangkat I/O jauh lebih rendah dibandingkan kecepatan prosesor pusat. Oleh karena itu, Anda harus menunggu sinyal siap untuk waktu yang sangat lama, terus-menerus melakukan polling pada jalur antarmuka yang sesuai untuk ada atau tidaknya sinyal yang diinginkan. Tidak masuk akal untuk mengirim perintah baru tanpa menunggu sinyal siap yang menunjukkan pelaksanaan perintah sebelumnya. Dalam mode jajak pendapat kesiapan, driver yang mengontrol proses pertukaran data dengan perangkat eksternal menjalankan perintah “periksa sinyal kesiapan” dalam satu putaran. Hingga sinyal siap muncul, pengemudi tidak melakukan apa pun. Dalam hal ini, tentu saja waktu CPU digunakan secara tidak rasional. Jauh lebih menguntungkan untuk mengeluarkan perintah I/O, melupakan perangkat I/O untuk sementara dan melanjutkan menjalankan program lain. Dan kemunculan sinyal kesiapan diartikan sebagai permintaan interupsi dari perangkat I/O. Sinyal kesiapan ini adalah sinyal permintaan interupsi.

Mode pertukaran interupsi pada dasarnya adalah mode kontrol asinkron. Agar tidak kehilangan koneksi dengan perangkat, hitungan mundur waktu dapat dimulai, di mana perangkat harus menjalankan perintah dan mengeluarkan sinyal permintaan interupsi. Jumlah waktu maksimum yang dibutuhkan perangkat I/O atau pengontrolnya untuk mengeluarkan sinyal permintaan interupsi sering disebut batas waktu yang dikonfigurasi. Jika waktu ini habis setelah mengeluarkan perintah berikutnya ke perangkat, dan perangkat masih tidak merespons, maka diambil kesimpulan bahwa koneksi dengan perangkat terputus dan tidak dapat lagi dikendalikan. Pengguna dan/atau tugas menerima pesan diagnostik yang sesuai.

Beras. 4.1. Kontrol I/O

Pengemudi. beroperasi dalam mode interupsi, mereka adalah sekumpulan modul perangkat lunak yang kompleks dan dapat memiliki beberapa bagian: bagian awal, satu atau lebih bagian kelanjutan, dan bagian penghentian.

Bagian startup memulai operasi I/O. Bagian ini dijalankan untuk menghidupkan perangkat I/O atau sekadar memulai operasi I/O lainnya.

Bagian lanjutan (mungkin ada beberapa jika algoritma kontrol pertukaran data rumit dan diperlukan beberapa interupsi untuk menyelesaikan satu operasi logis) melakukan pekerjaan utama transmisi data. Bagian lanjutan sebenarnya adalah pengendali interupsi utama. Antarmuka yang digunakan mungkin memerlukan beberapa rangkaian perintah kontrol untuk mengontrol I/O, dan perangkat biasanya hanya memiliki satu sinyal interupsi. Oleh karena itu, setelah mengeksekusi bagian interupsi berikutnya, supervisor interupsi harus mentransfer kendali ke bagian lain pada sinyal siap berikutnya. Hal ini dilakukan dengan mengubah alamat pemrosesan interupsi setelah bagian berikutnya dijalankan; jika hanya ada satu bagian interupsi, maka bagian itu sendiri mentransfer kendali ke satu atau beberapa modul pemrosesan.

Bagian terminasi biasanya mematikan perangkat I/O atau mengakhiri operasi saja.

Operasi I/O dapat dilakukan pada modul program yang meminta operasi dalam mode sinkron atau asinkron. Arti dari mode ini sama dengan panggilan sistem yang dibahas di atas - mode sinkron artinya modul perangkat lunak menangguhkan operasinya hingga operasi I/O selesai, dan dalam mode asinkron, modul program terus dijalankan dalam mode multiprogram secara bersamaan dengan operasi I/O. Perbedaannya adalah operasi I/O dapat dimulai tidak hanya oleh proses pengguna - dalam hal ini, operasi dilakukan sebagai bagian dari panggilan sistem, tetapi juga oleh kode kernel, misalnya kode subsistem memori maya untuk membaca halaman yang hilang dari memori.

Beras. 7.1. Dua mode I/O

Subsistem I/O harus menyediakan kepada kliennya (proses pengguna dan kode kernel) kemampuan untuk melakukan operasi I/O sinkron dan asinkron, bergantung pada kebutuhan pemanggil. Panggilan sistem Operasi I/O sering dibingkai sebagai prosedur sinkron karena fakta bahwa operasi tersebut memakan waktu lama dan proses pengguna atau thread masih harus menunggu hasil operasi diterima untuk melanjutkan pekerjaannya. Panggilan I/O internal dari modul kernel biasanya dijalankan sebagai prosedur asinkron, karena kode kernel memerlukan kebebasan untuk memilih apa yang harus dilakukan selanjutnya setelah operasi I/O diminta. Penggunaan prosedur asinkron menghasilkan solusi yang lebih fleksibel, karena berdasarkan panggilan asinkron, Anda selalu dapat membuat panggilan sinkron dengan membuat prosedur perantara tambahan yang memblokir eksekusi prosedur panggilan hingga I/O selesai. Terkadang proses aplikasi juga perlu melakukan operasi I/O asinkron, misalnya dengan arsitektur mikrokernel, ketika sebagian kode berjalan dalam mode pengguna sebagai proses aplikasi, namun menjalankan fungsi sistem operasi, membutuhkan kebebasan penuh untuk bertindak bahkan setelah memanggil operasi I/O.

Operasi input dan output memiliki kecepatan eksekusi yang lebih lambat dibandingkan jenis pemrosesan lainnya. Alasan perlambatan ini adalah faktor-faktor berikut:

Penundaan yang disebabkan oleh waktu yang dihabiskan untuk mencari trek dan sektor yang diperlukan pada perangkat akses acak (disk, CD).

Penundaan karena kecepatan pertukaran data yang relatif rendah perangkat fisik Dan memori sistem.

Keterlambatan transfer data melalui jaringan menggunakan file server, penyimpanan data, dan sebagainya.

Dalam semua contoh sebelumnya, operasi I/O dilakukan selaras dengan arus, jadi seluruh thread terpaksa menganggur sampai selesai.

Bab ini menunjukkan bagaimana Anda dapat mengatur agar thread terus dieksekusi tanpa menunggu I/O selesai, yang konsisten dengan eksekusi thread. asinkron masukan/keluaran. Berbagai teknik yang tersedia di Windows diilustrasikan dengan contoh.

Beberapa teknik ini digunakan dalam pengatur waktu tunggu, yang juga dijelaskan dalam bab ini.

Terakhir, dan yang terpenting, setelah mempelajari operasi I/O asinkron standar, kita dapat menggunakannya Port penyelesaian I/O, yang terbukti sangat berguna dalam membangun server terukur yang dapat mendukung jumlah besar klien tanpa membuat thread terpisah untuk masing-masing klien. Program 14.4 adalah versi modifikasi dari server yang dikembangkan sebelumnya yang memungkinkan penggunaan port penyelesaian I/O.

Ikhtisar Metode I/O Asinkron Windows

Windows menangani I/O asinkron menggunakan tiga teknik.

I/O multithread. Setiap thread dalam suatu proses atau serangkaian proses melakukan I/O sinkron normal, namun thread lain dapat terus mengeksekusi.

I/O yang tumpang tindih. Setelah memulai operasi baca, tulis, atau operasi I/O lainnya, thread melanjutkan eksekusinya. Jika sebuah thread memerlukan hasil I/O untuk terus dieksekusi, thread tersebut akan menunggu hingga handle yang sesuai tersedia atau peristiwa tertentu terjadi. Di Windows 9x, I/O yang tumpang tindih hanya didukung untuk perangkat serial, seperti pipa bernama.

Rutinitas penyelesaian (I/O yang diperluas) Ketika operasi I/O selesai, sistem akan memanggil spesial prosedur penyelesaian berjalan di dalam thread. I/O yang diperluas untuk file disk tidak didukung di Windows 9x.

I/O multithread menggunakan pipa bernama digunakan di server multithread yang dibahas di Bab 11. Program grepMT (Program 7.1) mengelola operasi I/O bersamaan yang melibatkan banyak file. Dengan demikian, kita telah memiliki sejumlah program yang melakukan I/O multi-thread dan dengan demikian menyediakan bentuk I/O asinkron.

I/O yang tumpang tindih adalah subjek dari bagian berikutnya, dan contoh implementasi konversi file (ASCII ke UNICODE) menggunakan teknik ini untuk menggambarkan kemampuan pemrosesan file berurutan. Untuk tujuan ini, versi modifikasi dari program 2.4 digunakan. Setelah I/O yang tumpang tindih, I/O yang diperluas menggunakan rutinitas penyelesaian akan dipertimbangkan.

Catatan

Metode I/O yang tumpang tindih dan diperluas sering kali sulit diterapkan, jarang memberikan manfaat kinerja apa pun, bahkan terkadang menyebabkan penurunan kinerja, dan dalam kasus I/O file hanya dapat bekerja dalam kondisi Kontrol jendela N.T. Masalah-masalah ini diatasi dengan bantuan benang, Oleh karena itu, banyak pembaca mungkin ingin langsung melompat ke bagian pengatur waktu tunggu dan port penyelesaian I/O. kembali ke bagian ini bila diperlukan. Di sisi lain, elemen I/O asinkron hadir dalam teknologi lama dan baru, sehingga metode ini masih perlu ditelusuri.

Oleh karena itu, teknologi COM pada platform NT5 mendukung pemanggilan metode asinkron, sehingga teknik ini mungkin berguna bagi banyak pembaca yang menggunakan atau berencana menggunakan teknologi COM. Selain itu, panggilan prosedur asinkron (Bab 10) memiliki banyak kesamaan dengan I/O yang diperluas, dan meskipun saya pribadi lebih suka menggunakan thread, orang lain mungkin lebih menyukai mekanisme ini.

I/O yang tumpang tindih

Hal pertama yang perlu Anda lakukan untuk mengimplementasikan I/O asinkron, baik tumpang tindih atau diperluas, adalah mengatur atribut tumpang tindih pada file atau deskriptor lainnya. Untuk melakukan ini, saat memanggil CreateFile atau fungsi lain yang membuat file, bernama pipa, atau deskriptor lainnya, Anda harus menentukan flag FILE_FLAG_OVERLAPPED.

Dalam kasus soket (Bab 12), baik dibuat menggunakan fungsi soket atau penerimaan, atribut tumpang tindih diatur secara default di Winsock 1.1, tetapi harus diatur secara eksplisit di Winsock 2.0. Soket yang tumpang tindih dapat digunakan secara asinkron di semua versi Windows.

Hingga saat ini, kita telah menggunakan struktur OVERLAPPED bersama dengan fungsi LockFileEx dan sebagai alternatif untuk menggunakan fungsi SetFilePointer (Bab 3), namun struktur tersebut juga merupakan elemen penting dari I/O yang tumpang tindih. Struktur ini bertindak sebagai parameter opsional saat memanggil empat fungsi di bawah ini, yang dapat diblokir saat operasi selesai.

Ingatlah bahwa ketika Anda menentukan flag FILE_FLAG_OVERLAPPED sebagai bagian dari parameter dwAttrsAndFlags (dalam kasus fungsi CreateFile) atau parameter dwOpen-Mode (dalam kasus fungsi CreateNamedPipe), file atau pipa yang sesuai hanya dapat digunakan dalam tumpang tindih mode. I/O yang tumpang tindih tidak berfungsi dengan saluran anonim.

Catatan

Dokumentasi untuk fungsi CreateFile menyebutkan bahwa penggunaan flag FILE_FLAG_NO_BUFFERING meningkatkan kinerja I/O yang tumpang tindih. Eksperimen hanya menunjukkan sedikit peningkatan kinerja (sekitar 15%, yang dapat diverifikasi dengan bereksperimen dengan Program 14.1), namun Anda harus memastikan bahwa ukuran total data yang dibaca saat melakukan operasi ReadFile atau WriteFile adalah kelipatan dari ukuran sektor disk .

Soket yang tumpang tindih

Salah satu inovasi terpenting di Windows Sockets 2.0 (Bab 12) adalah standarisasi I/O yang tumpang tindih. Secara khusus, soket tidak lagi dibuat secara otomatis sebagai deskriptor file yang tumpang tindih. Fungsi soket membuat pegangan yang tidak tumpang tindih. Untuk membuat soket yang tumpang tindih, Anda harus memanggil fungsi WSASocket, secara eksplisit meminta pembuatan saran yang tumpang tindih dengan menentukan nilai WSA_FLAG_OVERLAPPED untuk parameter dwFlags dari fungsi WSASocket.

SOCKET WSAAPI WSASocket(int iAddressFamily, int iSocketType, int iProtocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags);

Untuk membuat soket, gunakan fungsi WSASocket, bukan fungsi soket. Soket apa pun yang dikembalikan oleh penerimaan akan memiliki properti yang sama dengan argumennya.

Konsekuensi penggunaan I/O yang tumpang tindih

I/O yang tumpang tindih dilakukan secara asinkron. Hal ini mempunyai beberapa implikasi.

Operasi I/O yang tumpang tindih tidak diblokir. Fungsi ReadFile, WriteFile, TransactNamedPipe, dan ConnectNamedPipe kembali tanpa menunggu operasi I/O selesai.

Nilai yang dikembalikan oleh suatu fungsi tidak dapat digunakan sebagai kriteria keberhasilan atau kegagalan eksekusinya, karena operasi I/O belum selesai pada saat ini. Menunjukkan status kemajuan I/O memerlukan mekanisme lain.

Mengembalikan jumlah byte yang ditransfer juga tidak banyak gunanya karena transfer data mungkin belum selesai sepenuhnya. Untuk mendapatkan hal seperti ini informasi jendela harus menyediakan mekanisme lain.

Suatu program mungkin mencoba membaca atau menulis beberapa kali menggunakan pegangan file yang sama dan tumpang tindih. Oleh karena itu, penunjuk file yang sesuai dengan deskriptor tersebut juga tidak signifikan. Oleh karena itu, metode tambahan harus disediakan untuk memberikan indikasi posisi file untuk setiap operasi baca atau tulis. Dengan pipa bernama, karena sifat pemrosesan datanya yang berurutan, hal ini tidak menjadi masalah.

Program harus dapat menunggu (sinkronisasi) hingga I/O selesai. Jika ada beberapa operasi I/O yang tertunda terkait dengan pegangan yang sama, program harus dapat menentukan operasi mana yang telah selesai. Operasi I/O tidak selalu selesai dalam urutan yang sama seperti saat dimulainya.

Untuk mengatasi dua kesulitan terakhir yang tercantum di atas, digunakan struktur OVERLAPPED.

Struktur yang tumpang tindih

Dengan menggunakan struktur OVERLAPPED (ditentukan, misalnya, oleh parameter lpOverlapped dari fungsi ReadFile), Anda dapat menentukan informasi berikut:

Posisi file (64 bit) di mana operasi baca atau tulis harus dimulai, seperti yang dibahas di Bab 3.

Suatu peristiwa (direset secara manual) yang akan diberi sinyal setelah selesainya operasi terkait.

Di bawah ini adalah definisi dari struktur OVERLAPPED.

Bidang Offset dan OffsetHigh harus digunakan untuk menentukan posisi file (penunjuk), meskipun bagian tinggi dari penunjuk (OffsetHigh) adalah 0 dalam banyak kasus. Bidang Internal dan InternalHigh, yang dicadangkan untuk kebutuhan sistem, tidak boleh digunakan digunakan.

Parameter hEvent adalah deskriptor acara (dibuat menggunakan fungsi CreateEvent). Peristiwa ini dapat diberi nama atau tidak, tetapi memang demikian sebaiknya harus diatur ulang secara manual (lihat Bab 8) jika digunakan untuk I/O yang tumpang tindih; alasannya akan segera dijelaskan. Ketika operasi I/O selesai, kejadian memasuki keadaan sinyal.

Dalam kasus penggunaan lain yang mungkin, deskriptor hEvent adalah NULL; dalam hal ini, program mungkin menunggu hingga deskriptor file diberi sinyal, yang juga dapat bertindak sebagai objek sinkronisasi (lihat peringatan berikut). Sistem menggunakan status pensinyalan deskriptor file untuk melacak penyelesaian operasi jika deskriptor hEvent adalah NULL, yaitu objek sinkronisasi dalam hal ini adalah deskriptor file.

Catatan

Untuk kenyamanan, kita akan menggunakan istilah "pegangan file" untuk merujuk pada pegangan yang ditentukan dalam panggilan ke ReadFile, WriteFile, dan seterusnya, bahkan dalam kasus di mana yang sedang kita bicarakan tentang pegangan ke pipa atau perangkat bernama, bukan file.

Ketika pemanggilan fungsi I/O dilakukan, kejadian ini segera dihapus oleh sistem (diatur ke keadaan non-sinyal). Ketika operasi I/O selesai, kejadian tersebut diatur ke status alarm dan tetap berada di sana hingga digunakan oleh operasi I/O lainnya. Suatu kejadian harus direset secara manual jika beberapa thread menunggu untuk diberi sinyal (walaupun contoh kita hanya menggunakan satu thread), dan mungkin tidak berada dalam status menunggu saat operasi selesai.

Bahkan jika pegangan file sinkron (yaitu, dibuat tanpa flag FILE_FLAG_OVERLAPPED), struktur OVERLAPPED dapat berfungsi sebagai alternatif fungsi SetFilePointer untuk menentukan posisi dalam file. Dalam kasus ini, panggilan fungsi ReadFile atau panggilan lainnya tidak kembali hingga operasi I/O selesai. Kita telah memanfaatkan fitur ini di Bab 3. Perhatikan juga bahwa operasi I/O yang tertunda diidentifikasi secara unik melalui kombinasi deskriptor file dan struktur OVERLAPPED yang sesuai.

Di bawah ini tercantum beberapa peringatan yang perlu diperhatikan.

Hindari menggunakan kembali struktur OVERLAPPED ketika operasi I/O terkait, jika ada, belum selesai.

Demikian pula, hindari menggunakan kembali peristiwa yang ditentukan dalam struktur OVERLAPPED.

Jika ada beberapa permintaan tertunda untuk pegangan yang sama dan tumpang tindih, gunakan pegangan peristiwa daripada pegangan file untuk sinkronisasi.

Jika struktur atau peristiwa OVERLAPPED bertindak sebagai variabel otomatis dalam sebuah blok, pastikan bahwa blok tersebut tidak dapat keluar sebelum melakukan sinkronisasi dengan operasi I/O. Selain itu, untuk menghindari kebocoran sumber daya, berhati-hatilah saat menutup pegangan sebelum keluar dari blok.

Status I/O yang tumpang tindih

Fungsi ReadFile dan WriteFile, serta dua fungsi pipa bernama di atas, segera kembali ketika digunakan untuk melakukan operasi I/O yang tumpang tindih. Dalam kebanyakan kasus, operasi I/O belum selesai pada saat ini, dan nilai kembalian dari baca dan tulis akan menjadi FALSE. Fungsi GetLastError akan mengembalikan ERROR_IO_PENDING dalam situasi ini.

Setelah Anda selesai menunggu objek sinkronisasi (peristiwa atau mungkin deskriptor file) masuk ke status sinyal yang menunjukkan operasi telah selesai, Anda harus mengetahui berapa banyak byte yang ditransfer. Ini adalah tujuan utama dari fungsi GetOverlappedResult.

BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPWORD lpcbTransfer, BOOL bWait)

Spesifikasi operasi I/O tertentu disediakan oleh kombinasi pegangan dan struktur OVERLAPPED. Nilai TRUE dari bWait menetapkan bahwa fungsi GetOverlappedResult harus menunggu hingga operasi selesai; jika tidak, pengembalian dari fungsi harus segera dilakukan. Bagaimanapun, fungsi ini akan mengembalikan TRUE hanya setelah operasi berhasil diselesaikan. Jika nilai kembalian GetOverlappedResult adalah FALSE, maka GetLastError akan mengembalikan ERROR_IO_INCOMPLETE, sehingga fungsi ini dapat dipanggil untuk melakukan polling penyelesaian I/O.

Jumlah byte yang ditransfer disimpan dalam variabel *lpcbTransfer. Selalu pastikan bahwa struktur OVERLAPPED tetap tidak berubah sejak digunakan dalam operasi I/O yang tumpang tindih.

Membatalkan operasi I/O yang tumpang tindih

Fungsi CancelIO Boolean memungkinkan Anda membatalkan operasi I/O tumpang tindih yang tertunda terkait dengan pegangan yang ditentukan (fungsi ini hanya memiliki satu parameter). Semua operasi yang dimulai oleh thread pemanggil yang menggunakan pegangan dibatalkan. Operasi yang dimulai oleh thread lain tidak terpengaruh dengan pemanggilan fungsi ini. Operasi yang dibatalkan diselesaikan dengan kesalahan KESALAHAN OPERASI DIBATALKAN.

Contoh: Menggunakan File Handle sebagai Objek Sinkronisasi

I/O yang tumpang tindih sangat nyaman dan mudah diterapkan jika hanya ada satu operasi yang tertunda. Kemudian, untuk tujuan sinkronisasi, program tidak dapat menggunakan acara, melainkan deskriptor file.

Cuplikan kode berikut menunjukkan bagaimana program dapat memulai operasi baca untuk membaca bagian file, melanjutkan eksekusi untuk melakukan pemrosesan lainnya, dan kemudian memasuki keadaan menunggu pegangan file memasuki keadaan sinyal.

OVERLAPPED ov = ( 0, 0, 0, 0, NULL /* Event tidak digunakan. */ );
hF = BuatFile(…, FILE_FLAG_OVERLAPPED, …);
ReadFile(hF, Buffer, sizeof(Buffer), &nBaca, &ov);
/* Melakukan jenis pemrosesan lainnya. nBacaan belum tentu dapat diandalkan.*/
/* Tunggu hingga operasi pembacaan selesai. */
WaitForSingleObject(hF, INFINITE);
GetOverlappedResult(hF, &ov, &nBaca, FALSE);

Contoh: Konversi File Menggunakan Overlapping I/O dan Multiple Buffering

Program 2.4 (atou) melakukan konversi file ASCII ke UNICODE dengan pemrosesan sekuensial file, dan Bab 5 menunjukkan cara melakukan pemrosesan sekuensial yang sama dengan pemetaan file. Program 14.1 (atouOV) memecahkan masalah yang sama menggunakan I/O yang tumpang tindih dan beberapa buffer yang menyimpan catatan berukuran tetap.

Gambar 14.1 mengilustrasikan pengorganisasian suatu program dengan empat buffer berukuran tetap. Program ini diimplementasikan sehingga jumlah buffer dapat ditentukan menggunakan konstanta praprosesor simbolik, namun pada pembahasan berikut kita akan mengasumsikan bahwa ada empat buffer.

Pertama, program ini menginisialisasi semua elemen struktur OVERLAPPED yang menentukan peristiwa dan posisi dalam file. Setiap buffer input dan output memiliki struktur OVERLAPPED yang terpisah. Setelah ini, operasi baca yang tumpang tindih dimulai untuk masing-masing buffer masukan. Selanjutnya, dengan menggunakan fungsi WaitForMultipleObjects, program menunggu satu peristiwa yang menunjukkan selesainya pembacaan atau penulisan. Ketika operasi baca selesai, buffer masukan disalin dan diubah ke buffer keluaran yang sesuai, dan operasi tulis dimulai. Ketika penulisan selesai, operasi pembacaan berikutnya dimulai. Perhatikan bahwa peristiwa yang terkait dengan buffer masukan dan keluaran terletak dalam satu larik, yang digunakan sebagai argumen saat memanggil fungsi WaitForMultipleObjects.

Beras. 14.1. Model pembaruan file asinkron


Program 14.1. atouOV: Konversi file menggunakan I/O yang tumpang tindih
Konversikan file dari ASCII ke Unicode menggunakan I/O yang tumpang tindih. Program ini hanya bekerja pada Windows NT. */

#define MAX_OVRLP 4 /* Jumlah operasi I/O yang tumpang tindih.*/
#define REC_SIZE 0x8000 /* 32 KB: Ukuran rekaman minimum untuk memberikan kinerja yang dapat diterima. */

/* Setiap elemen array variabel didefinisikan di bawah */
/* dan struktur berkaitan dengan satu operasi yang belum selesai */
/* I/O tumpang tindih. */
DWORD nin, nout, ic, i;
Tumpang Tindih Tumpang Tindih, Tumpang Tindih;
/* Kebutuhan untuk menggunakan solid, array dua dimensi */
/* ditentukan oleh Fungsi WaitForMultipleObjects. */
/* Nilai 0 dari indeks pertama berhubungan dengan pembacaan, nilai 1 dengan penulisan.*/
/* Di masing-masing dari dua array buffer yang ditentukan di bawah, indeks pertama */
/* angka operasi I/O. */
LARGE_INTEGER CurPosIn, CurPosOut, Ukuran File;
/* Jumlah total record yang akan diproses, dihitung */
/* berdasarkan ukuran file masukan. Entri di akhir */
/* mungkin tidak lengkap. */
untuk (ic = 0; ic< MAX_OVRLP; ic++) {
/* Membuat event baca dan tulis untuk setiap struktur OVERLAPPED.*/
hEvents = OverLapIn.hEvent /* Membaca acara.*/
hEvents = OverLapOut.hEvent /* Merekam acara. */
= BuatEvent(NULL, BENAR, SALAH, NULL);
/* Posisi awal dalam file untuk setiap struktur OVERLAPPED. */
/* Memulai operasi baca yang tumpang tindih untuk struktur OVERLAPPED ini. */
if (CurPosIn.QuadPart< FileSize.QuadPart) ReadFile(hInputFile, AsRec, REC_SIZE, &nin, &OverLapIn);
/* Semua operasi baca dilakukan. Tunggu hingga event selesai dan segera reset. Peristiwa baca dan tulis disimpan dalam larik peristiwa yang bersebelahan. */
iTunggu =0; /* Jumlah selesai pada saat ini operasi masukan/keluaran. */
sementara (iTunggu< 2 * nRecord) {
ic = WaitForMultipleObjects(2 * MAX_OVRLP, hEvents, FALSE, INFINITE) – WAIT_OBJECT_0;
iTunggu++; /* Menambah penghitung operasi I/O yang telah selesai.*/
ResetAcara(hEvents);
/* Membaca selesai. */
GetOverlappedResult(hInputFile, &OverLapIn, &nin, FALSE);
untuk (saya =0; saya< REC_SIZE; i++) UnRec[i] = AsRec[i];
WriteFile(hOutputFile, UnRec, nin * 2, &nout, &OverLapOut);
/* Mempersiapkan pembacaan berikutnya, yang akan dimulai setelah operasi penulisan yang dimulai di atas selesai. */
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;
) lain jika (ic< 2 * MAX_OVRLP) { /* Операция записи завершилась. */
/* Mulai membaca. */
ic –= MAX_OVRLP; /* Mengatur indeks buffer keluaran. */
if (!GetOverlappedResult (hOutputFile, &OverLapOut, &nout, FALSE)) ReportError(_T("Baca kesalahan."), 0, TRUE);
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
if (CurPosIn.QuadPart< FileSize.QuadPart) {
/* Mulai operasi baru membaca. */
ReadFile(hInputFile, AsRec, REC_SIZE, &nin, &OverLapIn);
/* Menutup semua acara. */
untuk (ic = 0; ic< MAX_OVRLP; ic++) {

Program 14.1 hanya dapat berjalan pada Windows NT. Fasilitas I/O asinkron Windows 9x tidak mengizinkan penggunaan file disk. Lampiran B memberikan hasil dan komentar yang menunjukkan kinerja program atouOV yang relatif buruk. Eksperimen telah menunjukkan bahwa untuk mencapai kinerja yang dapat diterima, ukuran buffer harus minimal 32 KB, namun bahkan dalam kasus ini, I/O sinkron konvensional lebih cepat. Selain itu, kinerja program ini tidak meningkat pada kondisi SMP, sejak di dalam contoh ini, yang hanya memproses dua file, CPU bukanlah sumber daya penting.

I/O yang Diperluas Menggunakan Rutin Penyelesaian

Ada juga kemungkinan pendekatan lain untuk menggunakan objek sinkronisasi. Daripada meminta thread menunggu sinyal terminasi dari suatu event atau handle, sistem dapat memulai panggilan ke rutin terminasi yang ditentukan pengguna segera setelah operasi I/O selesai. Prosedur penyelesaian kemudian dapat memulai operasi I/O berikutnya dan melakukan tindakan apa pun yang diperlukan untuk memperhitungkan penggunaan sumber daya sistem. Prosedur penyelesaian panggilan balik tidak langsung ini mirip dengan panggilan prosedur asinkron yang digunakan di Bab 10 dan memerlukan penggunaan status tunggu yang dapat diwaspadai.

Bagaimana prosedur penghentian ditentukan dalam suatu program? Di antara parameter atau struktur data fungsi ReadFile dan WriteFile, tidak ada parameter yang dapat digunakan untuk menyimpan alamat prosedur penyelesaian. Namun, ada rangkaian fungsi I/O yang diperluas yang ditandai dengan akhiran "Ex" dan berisi parameter tambahan, dimaksudkan untuk menyampaikan alamat prosedur penyelesaian. Fungsi baca dan tulis masing-masing adalah ReadFileEx dan WriteFileEx. Selain itu, penggunaan salah satu fitur siaga berikut diperlukan.

I/O yang diperluas terkadang disebut masukan/keluaran tugas(I/O yang dapat diwaspadai). Lihat bagian berikut untuk informasi tentang cara menggunakan fitur lanjutan.

Catatan

Pada Windows 9x, I/O yang diperluas tidak dapat digunakan file disk dan port komunikasi. Pada saat yang sama, Windows 9x Extended I/O mampu bekerja dengan pipa bernama, kotak surat, soket, dan perangkat serial.

ReadFileEx, fungsi WriteFileEx dan prosedur penyelesaian

Fungsi baca dan tulis tingkat lanjut dapat digunakan bersama dengan pegangan membuka file, bernama pipa dan kotak surat, jika objek terkait dibuka (dibuat) dengan set flag FILE_FLAG_OVERLAPPED. Perhatikan bahwa tanda ini menyetel atribut pegangan, dan meskipun I/O yang tumpang tindih dan diperluas berbeda, tanda yang sama berlaku untuk pegangan kedua jenis I/O asinkron.

Soket yang tumpang tindih (Bab 12) dapat digunakan bersama dengan fungsi ReadFileEx dan WriteFileEx di semua versi Windows.

BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpcr)
BOOL WriteFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpcr)

Anda sudah familiar dengan kedua fungsi tersebut, hanya saja masing-masing fungsi memiliki parameter tambahan yang memungkinkan Anda menentukan alamat rutin terminasi.

Setiap fungsi harus dilengkapi dengan struktur OVERLAPPED, namun tidak perlu menyediakan elemen hEvent dari struktur ini; sistem mengabaikannya. Namun, elemen ini sangat berguna untuk menyampaikan informasi seperti nomor urut yang digunakan untuk membedakan operasi input/output individual, seperti yang ditunjukkan pada Program 14.2.

Dibandingkan dengan fungsi ReadFile dan WriteFile, Anda akan melihat bahwa fungsi yang diperluas tidak memerlukan parameter untuk menyimpan jumlah byte yang ditransfer. Informasi ini diteruskan ke fungsi penyelesaian yang harus disertakan dalam program.

Fungsi terminasi menyediakan parameter untuk jumlah byte, kode kesalahan, dan alamat struktur OVERLAPPED. Parameter terakhir ini diperlukan agar prosedur penyelesaian dapat menentukan operasi terutang mana yang telah selesai. Perhatikan bahwa peringatan sebelumnya tentang penggunaan kembali atau penghancuran struktur OVERLAPPED berlaku di sini juga dalam kasus I/O yang tumpang tindih.

BATALKAN WINAPI FileIOCompletionRutin(DWORD dwError, DWORD cbTransferred, LPOVERLAPPED lpo)

Seperti halnya fungsi CreateThread, saat memanggilnya, nama beberapa fungsi, namanya juga ditentukan FileIOCompletionRutin adalah pengganti, bukan nama sebenarnya dari prosedur penyelesaian.

Nilai parameter dwError dibatasi hingga 0 (berhasil) dan ERROR_HANDLE_EOF (mencoba membaca di luar batas). Struktur OVERLAPPED adalah struktur yang digunakan oleh panggilan ReadFileEx atau WriteFileEx yang telah selesai.

Sebelum rutinitas mematikan dipanggil oleh sistem, ada dua hal yang harus terjadi:

1. Operasi I/O harus selesai.

2. Thread pemanggil harus dalam keadaan standby, memberitahukan sistem bahwa ia perlu menjalankan rutinitas penyelesaian antrian.

Bagaimana cara thread memasuki kondisi siaga? Itu harus membuat panggilan eksplisit ke salah satu fungsi siaga yang dijelaskan di bagian berikutnya. Dengan demikian, thread menciptakan kondisi yang membuat eksekusi prematur dari prosedur terminasi menjadi tidak mungkin dilakukan. Sebuah thread dapat tetap dalam status standby hanya selama pemanggilan ke fungsi standby berlangsung; Setelah fungsi ini kembali, thread keluar dari status yang ditentukan.

Jika kedua kondisi ini terpenuhi, prosedur penyelesaian yang diantrekan sebagai hasil penyelesaian operasi I/O akan dijalankan. Rutinitas penyelesaian dijalankan pada thread yang sama yang membuat pemanggilan fungsi I/O awal dan berada dalam keadaan siaga. Oleh karena itu, thread harus memasuki keadaan siaga hanya ketika kondisi aman ada untuk pelaksanaan rutinitas penyelesaian.

Fungsi siaga

Ada total lima fungsi siaga, namun di bawah ini hanya prototipe dari tiga fungsi yang menarik bagi kami:

DWORD WaitForSingleObjectEx(HANDLE hObject, DWORD dwMilliseconds, BOOL bAlertable)
DWORD WaitForMultipleObjectsEx(DWORD cObjects, LPHANDLE lphObjects, BOOL fWaitAll, DWORD dwMilliseconds, BOOL bAlertable)
DWORD SleepEx(DWORD dwMillidetik, BOOL dapat diwaspadai)

Masing-masing fungsi peringatan memiliki tanda bAlertable, yang harus disetel ke TRUE dalam kasus I/O asinkron. Fungsi di atas merupakan perpanjangan dari fungsi Tunggu dan Tidur yang Anda kenal.

Durasi interval tunggu ditunjukkan, seperti biasa, dalam milidetik. Masing-masing dari ketiga fungsi ini kembali satu kali setiap dari situasi berikut:

Transisi deskriptor ke status sinyal, sehingga memenuhi persyaratan standar dari dua fungsi tunggu.

Batas waktu telah habis.

Semua rutinitas penyelesaian dalam antrian thread berhenti dijalankan dan bAlertable disetel ke TRUE. Prosedur penyelesaian dimasukkan ke dalam antrian ketika operasi I/O terkait telah selesai (Gambar 14.2).

Perhatikan bahwa tidak ada peristiwa yang terkait dengan struktur OVERLAPPED dalam fungsi ReadFileEx dan WriteFileEx, sehingga tidak ada pegangan yang ditentukan saat memanggil fungsi tunggu yang terkait langsung dengan operasi I/O tertentu. Pada saat yang sama, fungsi SleepEx tidak terkait dengan objek sinkronisasi, dan oleh karena itu fungsi ini paling mudah digunakan. Dalam kasus fungsi SleepEx, interval batas waktu biasanya diatur ke INFINITE, sehingga fungsi akan kembali hanya setelah satu atau lebih finalisator yang saat ini ada dalam antrean selesai dijalankan.

Menjalankan prosedur mematikan dan kembali dari fungsi siaga

Ketika operasi I/O yang diperluas selesai dijalankan, rutinitas penyelesaian terkait, dengan argumennya yang menentukan struktur OVERLAPPED, jumlah byte, dan kode kesalahan, dimasukkan dalam antrean untuk dieksekusi.

Semua rutinitas penyelesaian dalam antrian thread mulai dijalankan ketika thread memasuki keadaan siaga. Mereka dieksekusi satu per satu, tetapi tidak harus dalam urutan yang sama dengan penyelesaian operasi I/O. Pengembalian dari fungsi siaga hanya terjadi setelah prosedur penyelesaian telah kembali. Fitur ini penting untuk memastikan berfungsinya sebagian besar program dengan benar karena fitur ini mengasumsikan bahwa rutinitas terminasi mempunyai kesempatan untuk mempersiapkan penggunaan berikutnya dari struktur OVERLAPPED dan melakukan tindakan lain yang diperlukan untuk menempatkan program ke keadaan yang diketahui sebelum kembali dari keadaan siaga. negara.

Jika kembalinya fungsi SleepEx disebabkan oleh eksekusi satu atau lebih rutinitas penyelesaian antrean, nilai kembalian fungsi tersebut adalah WAIT_TO_COMPLETION, dan nilai yang sama akan dikembalikan oleh fungsi GetLastError yang dipanggil setelah salah satu fungsi tunggu kembali .

Sebagai kesimpulan, kami mencatat dua hal:

1. Saat memanggil salah satu fungsi siaga, gunakan INFINITE sebagai nilai parameter interval tidur. Jika tidak ada opsi batas waktu, fungsi akan kembali hanya setelah semua rutinitas terminasi selesai dieksekusi atau deskriptor telah memasuki status sinyal.

2. Untuk meneruskan informasi ke prosedur penyelesaian, biasanya menggunakan elemen data hEvent dari struktur OVERLAPPED, karena bidang ini diabaikan oleh OS.

Interaksi antara thread utama, rutinitas penyelesaian, dan fungsi siaga diilustrasikan pada Gambar. 14.2. Contoh ini memulai tiga pembacaan bersamaan, dua di antaranya selesai pada saat standby mulai dijalankan.

Beras. 14.2. I/O asinkron menggunakan rutinitas penyelesaian

Contoh: Mengonversi File Menggunakan I/O Tingkat Lanjut

Program 14.3 (atouEX) adalah versi yang didesain ulang dari program 14.1. Program-program ini menggambarkan perbedaan antara dua metode I/O asinkron. Program atouEx mirip dengan Program 14.1, tetapi program ini memindahkan sebagian besar kode untuk mengatur sumber daya ke dalam rutinitas finalizer dan menjadikan banyak variabel global sehingga finalizer dapat mengaksesnya. Namun, Lampiran B menunjukkan bahwa dalam hal kinerja, atouEx dapat bersaing dengan baik dengan metode lain yang tidak menggunakan pemetaan file, sedangkan atouOV lebih lambat.

Program 14.2. atouEx: Konversi file menggunakan I/O yang diperluas
Konversikan file dari ASCII ke Unicode menggunakan I/O LANJUTAN. */
/* atouEX file1 file2 */

#define REC_SIZE 8096 /* Ukuran blok tidak begitu penting untuk kinerja dibandingkan dengan atouOV. */
#tentukan UREC_SIZE 2 * REC_SIZE

statis VOID WINAPI ReadDone(DWORD, DWORD, LPOVERLAPPED);
statis VOID WINAPI WriteDone (DWORD, DWORD, LPOVERLAPPED);

/* Struktur OVERLAPPED pertama untuk membaca, dan struktur kedua untuk menulis. Struktur dan buffer dialokasikan untuk setiap operasi yang akan datang. */
Tumpang Tindih Tumpang Tindih, Tumpang Tindih;
CHAR AsRec;
WCHAR Rek;
MENANGANI hInputFile, hOutputFile;

int _tmain(int argc, LPTSTR argv) (
hInputFile = BuatFile(argv, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
hOutputFile = BuatFile(argv, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
FileSize.LowPart = GetFileSize(hInputFile, &FileSize.HighPart);
nRekam = Ukuran File.QuadPart / REC_SIZE;
if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;
untuk (ic = 0; ic< MAX_OVRLP; ic++) {
OverLapIn.hEvent = (MENANGANI)ic; /* Muat ulang acara. */
OverLapOut.hEvent = (MENANGANI)ic; /* Bidang. */
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;
if (CurPosIn.QuadPart< FileSize.QuadPart) ReadFileEx(hInputFile, AsRec, REC_SIZE, &OverLapIn , ReadDone);
CurPosIn.QuadPart += (PANJANG)REC_SIZE;
/* Semua operasi baca dilakukan. Masuk ke status standby dan tetap di sana sampai semua catatan telah diproses.*/
sementara (nSelesai< 2 * nRecord) SleepEx(INFINITE, TRUE);
_tprintf(_T("Konversi ASCII ke Unicode selesai.\n"));

static VOID WINAPI ReadDone (Kode DWORD, DWORD nBytes, LPOVERLAPPED pOv) (
/* Membaca selesai. Konversi data dan mulai perekaman. */
LARGE_INTEGER CurPosIn, CurPosOut;
/* Memproses penulisan dan memulai operasi penulisan. */
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
CurPosOut.QuadPart = (CurPosIn.QuadPart / REC_SIZE) * UREC_SIZE;
OverLapOut.Offset = CurPosOut.LowPart;
OverLapOut.OffsetHigh = CurPosOut.HighPart;
/* Mengonversi entri dari ASCII ke Unicode. */
untuk (saya = 0; saya< nBytes; i++) UnRec[i] = AsRec[i];
WriteFileEx(hOutputFile, UnRec, nBytes*2, &OverLapOut, WriteDone);
/* Siapkan struktur OVERLAPPED untuk pembacaan berikutnya. */
CurPosIn.QuadPart += REC_SIZE * (PANJANG)(MAX_OVRLP);
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;

static VOID WINAPI WriteDone (Kode DWORD, DWORD nBytes, LPOVERLAPPED pOv) (
/* Perekaman selesai. Memulai operasi baca berikutnya. */
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
if (CurPosIn.QuadPart< FileSize.QuadPart) {
ReadFileEx(hInputFile, AsRec, REC_SIZE, &OverLapIn, ReadDone);

I/O asinkron menggunakan banyak thread

I/O yang tumpang tindih dan diperluas memungkinkan I/O dilakukan secara asinkron dalam satu thread, meskipun OS membuat threadnya sendiri untuk mendukung fungsi ini. Dalam satu atau lain bentuk, metode jenis ini sering digunakan di banyak sistem operasi awal untuk mendukung bentuk terbatas dalam melakukan operasi asinkron pada sistem thread tunggal.

Namun, Windows menyediakan dukungan multi-threading, sehingga dimungkinkan untuk mencapai efek yang sama dengan melakukan operasi I/O sinkron pada beberapa thread yang berjalan secara independen. Kemampuan ini sebelumnya telah didemonstrasikan menggunakan server multithread dan program grepMT (Bab 7). Selain itu, thread menyediakan cara yang secara konseptual konsisten dan mungkin lebih sederhana untuk melakukan operasi I/O asinkron. Alternatif metode yang digunakan dalam Program 14.1 dan 14.2 adalah memberikan setiap thread penanganan filenya sendiri, sehingga setiap thread dapat memproses setiap record keempat secara sinkron.

Cara penggunaan thread ini didemonstrasikan dalam program atouMT, yang tidak diberikan dalam buku, namun disertakan dalam materi yang diposting di situs Web. atouMT tidak hanya dapat dijalankan pada versi Windows apa pun, namun juga lebih sederhana dibandingkan salah satu dari dua program I/O asinkron karena penghitungan penggunaan sumber daya tidak terlalu rumit. Setiap thread hanya memelihara buffernya sendiri di tumpukannya sendiri dan melakukan loop melalui serangkaian operasi baca, konversi, dan tulis yang sinkron. Pada saat yang sama, kinerja program tetap pada tingkat yang cukup tinggi.

Catatan

Program atouMT.c, yang terletak di situs Web, berisi komentar tentang beberapa kemungkinan jebakan yang mungkin menunggu Anda ketika mengizinkan beberapa thread untuk mengakses file yang sama secara bersamaan. Secara khusus, semua pegangan file individual harus dibuat menggunakan fungsi CreateHandle, bukan fungsi DuplikatHandle.

Secara pribadi, saya lebih suka menggunakan pemrosesan file multi-utas daripada I/O asinkron. Thread lebih mudah diprogram dan memberikan kinerja yang lebih baik dalam banyak kasus.

Ada dua pengecualian terhadap aturan umum ini. Yang pertama, seperti yang ditunjukkan sebelumnya dalam bab ini, berkaitan dengan situasi di mana mungkin hanya ada satu operasi yang belum diselesaikan, dan deskriptor file dapat digunakan untuk tujuan sinkronisasi. Pengecualian kedua yang lebih penting terjadi pada kasus port penyelesaian I/O asinkron, yang akan dibahas di akhir bab ini.

Tunggu pengatur waktu

Windows NT mendukung pengatur waktu yang dapat ditunggu, yang merupakan jenis objek kernel yang menunggu.

Anda selalu dapat membuat sinyal sinkronisasi Anda sendiri dengan membuat thread sinkronisasi yang menetapkan peristiwa sebagai akibat dari bangun setelah memanggil fungsi Tidur. Dalam program serverNP (Program 11.3), server juga menggunakan timing thread untuk menyiarkan nama salurannya secara berkala. Oleh karena itu, pengatur waktu tunggu menyediakan, meskipun agak berlebihan, cara yang nyaman mengatur pelaksanaan tugas secara berkala atau sesuai dengan jadwal tertentu. Secara khusus, pengatur waktu tunggu dapat dikonfigurasi sehingga sinyal dihasilkan pada waktu yang ditentukan secara ketat.

Pengatur waktu tunggu dapat berupa pengatur waktu sinkronisasi atau pengatur waktu notifikasi yang disetel ulang secara manual. Pengatur waktu sinkronisasi dikaitkan dengan fungsi panggilan tidak langsung yang mirip dengan prosedur penyelesaian I/O yang diperluas, sedangkan fungsi tunggu digunakan untuk menyinkronkan dengan pengatur waktu notifikasi yang dapat disetel ulang secara manual.

Pertama, Anda perlu membuat pengatur waktu menggunakan fungsi CreateWaitableTimer.

MENANGANI CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCTSTR lpTimerName);

Parameter kedua, bManualReset, menentukan jenis pengatur waktu yang harus dibuat - sinkronisasi atau pemberitahuan. Program 14.3 menggunakan pengatur waktu sinkronisasi, tetapi dengan mengubah komentar dan pengaturan parameter Anda dapat dengan mudah mengubahnya menjadi pengatur waktu notifikasi. Perhatikan bahwa ada juga fungsi OpenWaitableTimer yang bisa menggunakan nama opsional yang disediakan oleh argumen ketiga.

Pengatur waktu awalnya dibuat dalam keadaan tidak aktif, tetapi menggunakan fungsi SetWaitableTimer Anda dapat mengaktifkannya dan menentukan waktu tunda awal, serta lamanya waktu antara sinyal yang dihasilkan secara berkala.

BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG IPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, BOOL fResume);

hTimer adalah pegangan valid untuk pengatur waktu yang dibuat menggunakan fungsi CreateWaitableTimer.

Parameter kedua, yang ditunjukkan oleh penunjuk pDueTime, dapat mengambil nilai positif yang sesuai dengan waktu absolut atau nilai negatif yang sesuai dengan waktu relatif, dengan nilai aktual dinyatakan dalam satuan waktu 100 nanodetik dan formatnya dijelaskan oleh struktur FILETIME. Ketik variabel FILETIME diperkenalkan di Bab 3 dan sudah kami gunakan di Bab 6 dalam program timep (Program 6.2).

Interval antar sinyal, yang ditentukan pada parameter ketiga, dinyatakan dalam milidetik. Jika nilai ini disetel ke 0, pengatur waktu hanya diberi sinyal satu kali. Jika parameter ini positif, maka pengatur waktu bersifat periodik dan menyala secara berkala hingga dihentikan dengan memanggil fungsi CancelWaitableTimer. Nilai negatif dari interval yang ditentukan tidak diperbolehkan.

Parameter keempat, pfnCompletionRoutine, digunakan dalam kasus pengatur waktu sinkronisasi dan menentukan alamat rutinitas penyelesaian yang dipanggil ketika pengatur waktu memasuki keadaan sinyal. dan disediakan agar thread masuk ke kondisi standby. Saat prosedur ini dipanggil, salah satu argumennya adalah penunjuk yang ditentukan oleh parameter kelima, plArgToComplretionRoutine.

Setelah menyetel pengatur waktu sinkronisasi, Anda dapat mengalihkan thread ke status siaga dengan memanggil fungsi SleepEx agar rutinitas terminasi dapat dipanggil. Dalam kasus pengatur waktu notifikasi yang disetel ulang secara manual, Anda harus menunggu hingga pengatur waktu memasuki status sinyal. Pegangan akan tetap dalam keadaan sinyal hingga panggilan berikutnya ke fungsi SetWaitableTimer. Versi lengkap Program 14.3, yang terletak di situs Web, memberi Anda kesempatan untuk melakukan eksperimen Anda sendiri menggunakan pengatur waktu pilihan Anda dalam kombinasi dengan prosedur penghentian atau menunggu pegangan pengatur waktu masuk ke keadaan sinyal, menghasilkan empat kombinasi berbeda.

Parameter terakhir, fResume, dikaitkan dengan mode hemat daya. Untuk mendapatkan lebih banyak informasi rinci Silakan merujuk ke dokumentasi bantuan mengenai masalah ini.

Fungsi CancelWaitableTimer digunakan untuk membatalkan fungsi SetWaitableTimer yang dipanggil sebelumnya, namun tidak mengubah status sinyal pengatur waktu. Untuk melakukan ini, Anda perlu memanggil fungsi SetWaitableTimer lagi.

Contoh: Menggunakan Timer Tunggu

Program 14.3 mendemonstrasikan penggunaan pengatur waktu tunggu untuk menghasilkan sinyal periodik.

Program 14.3. TimeBeep: menghasilkan sinyal periodik
/* Bab 14. Bunyi Bip Waktu. Pemberitahuan suara berkala. */
/* Penggunaan: Periode TimeBeep (dalam milidetik). */

Penangan BOOL WINAPI statis (DWORD CntrlEvent);
Pager APIENTRY VOID statis (LPVOID, DWORD, DWORD);
BOOL statis yang mudah menguap Keluar = FALSE;

int _tmain(int argc, LPTSTR argv) (
/* Mencegat kombinasi tombol untuk menghentikan operasi. Lihat bab 4.*/
SetConsoleCtrlHandler(Handler, BENAR);
DueTime.QuadPart = –(PANJANG)Periode * 10000;
/* Parameter DueTime bernilai negatif untuk periode waktu habis pertama dan relatif terhadap waktu saat ini. Masa tunggu diukur dalam ms (10 -3 detik), dan DueTime diukur dalam satuan 100 ns (10 -7 detik) untuk mengakomodasi tipe FILETIME. */
hTimer = CreateWaitableTimer(NULL, FALSE /* "Pengatur waktu sinkronisasi" */, NULL);
SetWaitableTimer(hTimer, &DueTime, Periode, Pager, &Count, TRUE);
_tprintf(_T("Hitungan = %d\n"), Hitung);
/* Nilai penghitung bertambah dalam prosedur pengatur waktu. */
/* Masuk ke kondisi standby. */
_tprintf(_T("Selesai. Penghitung = %d"), Hitung);

Pager APIENTRY VOID statis (LPVOID lpCount, DWORD dwTimerLowValue, DWORD dwTimerHighValue) (
*(LPDWORD)lpCount = *(LPDWORD)lpCount + 1;
_tprintf(_T("Menghasilkan nomor sinyal: %d\n"), *(LPDWORD) lpCount);
Kipas(1000 /* Frekuensi. */, 250 /* Durasi (ms). */);

Pengendali BOOL WINAPI(Acara Cntrl DWORD) (
_tprintf(_T("Matikan\n"));