Contoh Delphi Thread Pool Menggunakan AsyncCalls

Ini adalah proyek pengujian saya berikutnya untuk melihat apa yang akan membuat pustaka threading untuk Delphi sesuai untuk tugas "pemindaian file" yang ingin saya proses dalam beberapa utas / dalam kumpulan utas.

Untuk mengulangi tujuan saya: ubah "pemindaian file" berurutan dari 500-2000 + file dari pendekatan yang tidak berulir menjadi yang berulir. Saya seharusnya tidak memiliki 500 utas yang berjalan pada satu waktu, jadi ingin menggunakan kumpulan utas. Thread pool adalah kelas seperti antrian yang memberi makan sejumlah utas berjalan dengan tugas berikutnya dari antrian.

Upaya pertama (sangat mendasar) dilakukan dengan hanya memperluas kelas TThread dan mengimplementasikan metode Execute (parser string berulir saya).

Karena Delphi tidak memiliki kelas thread pool diimplementasikan di luar kotak, dalam upaya kedua saya, saya sudah mencoba menggunakan OmniThreadLibrary oleh Primoz Gabrijelcic.

OTL luar biasa, memiliki banyak cara untuk menjalankan tugas di latar belakang, cara untuk pergi jika Anda ingin memiliki pendekatan "api-dan-lupakan" untuk menyerahkan eksekusi ulir potongan kode Anda.

instagram viewer

AsyncCalls oleh Andreas Hausladen

Catatan: apa yang mengikuti akan lebih mudah diikuti jika Anda pertama kali mengunduh kode sumber.

Sambil menjelajahi lebih banyak cara untuk menjalankan beberapa fungsi saya di threaded, saya memutuskan untuk juga mencoba unit "AsyncCalls.pas" yang dikembangkan oleh Andreas Hausladen. Andy AsyncCalls - Panggilan fungsi Asynchronous unit adalah pustaka lain yang dapat digunakan pengembang Delphi untuk meringankan rasa sakit menerapkan pendekatan berulir untuk mengeksekusi beberapa kode.

Dari blog Andy: Dengan AsyncCalls, Anda dapat menjalankan banyak fungsi pada saat bersamaan dan menyinkronkannya di setiap titik dalam fungsi atau metode yang memulai... Unit AsyncCalls menawarkan berbagai prototipe fungsi untuk memanggil fungsi asinkron... Ini mengimplementasikan thread pool! Instalasi sangat mudah: cukup gunakan panggilanink dari unit Anda dan Anda memiliki akses instan ke hal-hal seperti "jalankan di utas terpisah, sinkronkan UI utama, tunggu sampai selesai".

Selain bebas untuk menggunakan (lisensi MPL) AsyncCalls, Andy juga sering menerbitkan perbaikan sendiri untuk Delphi IDE seperti "Delphi Mempercepat"dan"DDevExtensions"Saya yakin Anda pernah mendengar (jika belum menggunakan).

AsyncCalls In Action

Intinya, semua fungsi AsyncCall mengembalikan antarmuka IAsyncCall yang memungkinkan untuk menyinkronkan fungsi. IAsnycCall memaparkan metode berikut:

 //v 2.98 dari asynccalls.pas
IAsyncCall = antarmuka
// menunggu sampai fungsi selesai dan mengembalikan nilai kembali
fungsi Sinkronisasi: Integer;
// mengembalikan True ketika fungsi asinkron selesai
fungsi Selesai: Boolean;
// mengembalikan nilai pengembalian fungsi asinkron, ketika Selesai adalah BENAR
fungsi ReturnValue: Integer;
// memberitahu AsyncCalls bahwa fungsi yang ditugaskan tidak boleh dieksekusi di threa saat ini
prosedur ForceDifferentThread;
akhir;

Berikut ini contoh panggilan ke metode yang mengharapkan dua parameter integer (mengembalikan IAsyncCall):

 Panggilan TAsync. Meminta (AsyncMethod, i, Random (500));
fungsi TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: integer): integer;
mulai
hasil: = sleepTime;
Tidur (sleepTime);
Panggilan TAsync. VCLInvoke (
prosedur
mulai
Log (Format ('selesai> nr:% d / tugas:% d / tidur:% d', [tasknr, asyncHelper. TaskCount, sleepTime]));
akhir);
akhir;

Panggilan TAsync. VCLInvoke adalah cara untuk melakukan sinkronisasi dengan utas utama Anda (utas utama aplikasi - antarmuka pengguna aplikasi Anda). VCLInvoke segera kembali. Metode anonim akan dieksekusi di utas utama. Ada juga VCLSync yang kembali ketika metode anonim dipanggil di utas utama.

Thread Pool di AsyncCalls

Kembali ke tugas "pemindaian file" saya: saat mengumpankan (dalam for for loop) kumpulan utas panggilan dengan serangkaian TAsyncCalls. Invoke () panggilan, tugas akan ditambahkan ke internal pool dan akan dieksekusi "ketika saatnya tiba" (ketika panggilan yang sebelumnya ditambahkan telah selesai).

Tunggu Semua IAsyncCalls Untuk Menyelesaikan

Fungsi AsyncMultiSync yang didefinisikan dalam asnycalls menunggu panggilan async (dan pegangan lainnya) selesai. Ada beberapa kelebihan beban cara untuk memanggil AsyncMultiSync, dan inilah yang paling sederhana:

fungsi AsyncMultiSync (const Daftar: susunan dari IAsyncCall; WaitAll: Boolean = Benar; Milidetik: Kardinal = TAK TERBATAS): Kardinal; 

Jika saya ingin "tunggu semua" diimplementasikan, saya perlu mengisi larik IAsyncCall dan melakukan AsyncMultiSync dalam irisan 61.

Pembantu Panggilan Asnyc saya

Inilah bagian dari TAsyncCallsHelper:

PERINGATAN: kode parsial! (kode lengkap tersedia untuk diunduh)
menggunakan AsyncCalls;
Tipe
TIAsyncCallArray = susunan dari IAsyncCall;
TIAsyncCallArrays = susunan dari TIAsyncCallArray;
TAsyncCallsHelper = kelas
pribadi
fTasks: TIAsyncCallArrays;
Properti Tugas: TIAsyncCallArrays Baca fTasks;
publik
prosedur AddTask (const hubungi: IAsyncCall);
prosedur Tunggu Semua;
akhir;
PERINGATAN: kode parsial!
prosedur TAsyncCallsHelper. Tunggu Semua;
var
i: integer;
mulai
untuk i: = Tinggi (Tugas) ke Rendah (Tugas) melakukan
mulai
Panggilan Async. AsyncMultiSync (Tugas [i]);
akhir;
akhir;

Dengan cara ini saya bisa "menunggu semua" dalam potongan 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - yaitu menunggu array dari IAsyncCall.

Dengan kode di atas, kode utama saya untuk memberi makan kolam utas terlihat seperti:

prosedur TAsyncCallsForm.btnAddTasksClick (Pengirim: TObject);
const
nrItems = 200;
var
i: integer;
mulai
asyncHelper. MaxThreads: = 2 * Sistem. CPUCount;
ClearLog ('awal');
untuk i: = 1 ke nrItems melakukan
mulai
asyncHelper. AddTask (TAsyncCalls. Invoke (AsyncMethod, i, Random (500)));
akhir;
Log ('semua dalam');
// tunggu semua
//asyncHelper.WaitAll;
// atau izinkan pembatalan semua yang tidak dimulai dengan mengklik tombol "Batalkan Semua":

sementara TIDAK asyncHelper. Semua selesai melakukan Aplikasi. Pesan Proses;
Log ('selesai');
akhir;

Batalkan semua? - Harus Mengubah AsyncCalls.pas :(

Saya juga ingin memiliki cara "membatalkan" tugas-tugas yang ada di kolam tetapi sedang menunggu eksekusi mereka.

Sayangnya, AsyncCalls.pas tidak menyediakan cara sederhana untuk membatalkan tugas setelah tugas ditambahkan ke kumpulan utas. Tidak ada IAsyncCall. Batalkan atau IAsyncCall. DontDoIfNotAlreadyExecuting atau IAsyncCall. NeverMindMe.

Agar ini berfungsi, saya harus mengubah AsyncCalls.pas dengan mencoba mengubahnya sesedikit mungkin - jadi bahwa ketika Andy merilis versi baru saya hanya perlu menambahkan beberapa baris untuk memiliki ide "Batalkan tugas" saya kerja.

Inilah yang saya lakukan: Saya telah menambahkan "prosedur Batalkan" ke IAsyncCall. Prosedur Batal menetapkan bidang "FCancelled" (ditambahkan) yang akan diperiksa ketika kumpulan akan memulai menjalankan tugas. Saya perlu sedikit mengubah IAsyncCall. Selesai (sehingga laporan panggilan selesai bahkan ketika dibatalkan) dan TAsyncCall. Prosedur InternExecuteAsyncCall (tidak mengeksekusi panggilan jika telah dibatalkan).

Kamu bisa memakai WinMerge untuk dengan mudah menemukan perbedaan antara asynccall.pas asli Andy dan versi saya yang diubah (termasuk dalam unduhan).

Anda dapat mengunduh kode sumber lengkap dan menjelajahi.

Pengakuan

MEMPERHATIKAN! :)

Itu BatalkanInvokasi metode menghentikan AsyncCall agar tidak dipanggil. Jika AsyncCall sudah diproses, panggilan ke CancelInvocation tidak berpengaruh dan fungsi Dibatalkan akan mengembalikan False karena AsyncCall tidak dibatalkan.
Itu Dibatalkan metode mengembalikan True jika AsyncCall dibatalkan oleh CancelInvocation.
Itu Lupa metode memutuskan tautan antarmuka IAsyncCall dari AsyncCall internal. Ini berarti bahwa jika referensi terakhir ke antarmuka IAsyncCall hilang, panggilan asinkron akan tetap dieksekusi. Metode antarmuka akan mengeluarkan pengecualian jika dipanggil setelah memanggil Lupa. Fungsi async tidak boleh memanggil ke utas utama karena bisa dijalankan setelah TThread. Mekanisme Sinkronisasi / Antrian ditutup oleh RTL yang dapat menyebabkan kunci mati.

Namun, perhatikan bahwa Anda masih dapat memanfaatkan AsyncCallsHelper saya jika Anda harus menunggu semua panggilan async untuk diakhiri dengan "asyncHelper. Tunggu Semua "; atau jika Anda perlu "Batalkan Semua".

instagram story viewer