Bu benim Delphi için hangi iplik kitaplığı benim "dosya tarama" görev için en iyi paketi görmek için bir sonraki test projemde birden çok iş parçacığı / bir iş parçacığı havuzunda işlemek istiyorum.
Hedefimi tekrarlamak için: 500-2000 + dosyadan oluşan sıralı "dosya taramamı", iş parçacığı olmayan yaklaşımdan iş parçacığına dönüştür. Bir seferde çalışan 500 iş parçacığı olmamalı, bu nedenle bir iş parçacığı havuzu kullanmak istiyorum. İş parçacığı havuzu, sıradaki bir sonraki görevle çalışan bir dizi iş parçacığını besleyen sıra benzeri bir sınıftır.
İlk (çok temel) girişim, TThread sınıfının genişletilmesi ve Execute yönteminin (dişli dizgi ayrıştırıcım) uygulanmasıyla gerçekleştirildi.
Delphi kutunun dışında uygulanan bir iş parçacığı havuzu sınıfı olmadığından, ikinci denememde Primoz Gabrijelcic tarafından OmniThreadLibrary kullanmayı denedim.
OTL harika, arka planda bir görevi yürütmek için milyonlarca yolu vardır, kodunuzun parçalarının yürütülmesi için "ateşle ve unut" yaklaşımına sahip olmak istiyorsanız gitmek için bir yol.
Andreas Hausladen tarafından AsyncCalls
Not: Kaynak kodu ilk kez indirirseniz, aşağıdakileri takip etmek daha kolay olacaktır.
İşlevlerimin bazılarını dişli bir şekilde yürütmenin daha fazla yolunu keşfederken, Andreas Hausladen tarafından geliştirilen "AsyncCalls.pas" birimini de denemeye karar verdim. Andy'nin AsyncCalls - Eşzamansız işlev çağrıları birim, Delphi geliştiricisinin bazı kodların yürütülmesi için dişli yaklaşımın uygulanmasının acısını hafifletmek için kullanabileceği başka bir kütüphanedir.
Andy'nin blogundan: AsyncCalls ile aynı anda birden fazla işlevi çalıştırabilir ve bunları başlatan işlev veya yöntemdeki her noktada senkronize edebilirsiniz... AsyncCalls birimi, eşzamansız işlevleri çağırmak için çeşitli işlev prototipleri sunar. Bir iş parçacığı havuzu uygular! Kurulum süper kolaydır: sadece ünitelerinizden herhangi birinden asyncalls kullanın ve "ayrı bir iş parçacığında yürüt, ana kullanıcı arayüzünü senkronize et, bitene kadar bekle" gibi şeylere anında erişebilirsin.
Kullanımı ücretsiz (MPL lisansı) AsyncCalls'ın yanı sıra, Andy ayrıca Delphi IDE için kendi düzeltmelerini sık sık "Delphi Hızlandır" ve "DDevExtensions"Eminim duymuşsunuzdur (daha önce kullanmadıysanız).
AsyncCalls İş Başında
Temelde, tüm AsyncCall fonksiyonları, fonksiyonları senkronize etmeyi sağlayan bir IAsyncCall arayüzü döndürür. IAsnycCall aşağıdaki yöntemleri sunar:
//v 2.98 asynccalls.pas
IAsyncCall = arayüz
// işlev bitene kadar bekler ve dönüş değerini döndürür
işlev Senk: Tamsayı;
// eşzamansız işlevi bittiğinde True değerini döndürür
fonksiyon Bitirdi: Boolean;
// Bitti DOĞRU olduğunda eşzamansız işlevinin dönüş değerini döndürür
işlev ReturnValue: Integer;
// AsyncCalls'a atanan işlevin geçerli tehditte çalıştırılmaması gerektiğini söyler
yordam ForceDifferentThread;
son;
Aşağıda, iki tamsayı parametresini (IAsyncCall döndüren) bekleyen bir yönteme örnek çağrı verilmiştir:
TAsyncCalls. Invoke (AsyncMethod, i, Rastgele (500));
fonksiyon TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: integer): tamsayı;
başla
sonuç: = sleepTime;
Uyku (uyku Zamanı);
TAsyncCalls. VCLInvoke (
prosedür
başla
Günlük (Biçim ('tamamlandı> nr:% d / görevler:% d / uyudum:% d', [tasknr, asyncHelper. GörevCount, sleepTime]));
son);
son;
TAsyncCalls. VCLInvoke, ana iş parçanızla (uygulamanın ana iş parçacığı - uygulama kullanıcı arabirimi) eşitleme yapmanın bir yoludur. VCLInvoke hemen geri döner. Anonim yöntem ana iş parçacığında yürütülür. Ayrıca ana iş parçacığında anonim yöntem çağrıldığında dönen VCLSync vardır.
Konu Havuzu AsyncCalls içinde
Benim "dosya tarama" görevime geri: (for döngüsü içinde) zaman asynccalls TAsyncCalls serisi ile iş parçacığı havuzu beslerken. Çağrıları çağır (), görevler dahili havuza eklenir ve "zaman geldiğinde" (daha önce eklenen çağrılar bittiğinde) yürütülür.
Tüm IAsyncCall'ları Bitirmek İçin Bekleyin
Asnyccalls içinde tanımlanan AsyncMultiSync işlevi, async çağrılarının (ve diğer tutamaçların) bitmesini bekler. Birkaç tane var aşırı AsyncMultiSync'i çağırmanın yolları ve işte en basit olanı:
fonksiyon AsyncMultiSync (const Liste: dizisi IAsyncCall; WaitAll: Boolean = Doğru; Milisaniye: Kardinal = SONSUZ): Kardinal;
"Tüm beklemek" uygulanan olmasını istiyorsanız, IAsyncCall bir dizi doldurmak ve 61 dilimde AsyncMultiSync yapmak gerekir.
AsnycCalls Yardımcısı
İşte TAsyncCallsHelper'ın bir parçası:
UYARI: kısmi kod! (tam kod indirilebilir)
kullanımları AsyncCalls;
tip
TIAsyncCallArray = dizisi IAsyncCall;
TIAsyncCallArrays = dizisi TIAsyncCallArray;
TAsyncCallsHelper = sınıf
özel
f Görevler: TIAsyncCallArrays;
Emlak Görevler: TIAsyncCallArrays okumak fTasks;
halka açık
prosedür Görev ekle(const çağrı: IAsyncCall);
prosedür WaitAll;
son;
UYARI: kısmi kod!
prosedür TAsyncCallsHelper. WaitAll;
var
i: tam sayı;
başla
için i: = Yüksek (Görevler) aşağı Düşük (Görevler) yapmak
başla
AsyncCalls. AsyncMultiSync (Görevler [i]);
son;
son;
Bu şekilde 61 parçacığında "tümünü bekleyebilirim" (MAXIMUM_ASYNC_WAIT_OBJECTS) - yani IAsyncCall dizilerini bekliyorum.
Yukarıdakilerle, iş parçacığı havuzunu beslemek için ana kodum şöyle:
prosedür TAsyncCallsForm.btnAddTasksClick (Gönderen: TObject);
const
nrItems = 200;
var
i: tam sayı;
başla
asyncHelper. MaxThreads: = 2 * Sistem. CPUCount;
ClearLog ( 'başlangıç');
için i: = 1 - nrItems yapmak
başla
asyncHelper. AddTask (TAsyncCalls. Invoke (AsyncMethod, i, Rastgele (500)));
son;
Log ('all in');
// hepsini bekle
//asyncHelper.WaitAll;
// veya "Tümünü İptal Et" düğmesini tıklayarak başlatılmayan her şeyin iptal edilmesine izin verin:
DEĞİL iken asyncHelper. Herşey bitti yapmak Uygulama. ProcessMessages;
Günlüğü ( 'bitmiş');
son;
Hepsini iptal et? - AsyncCalls.pas'ı değiştirmek zorundasınız :(
Ben de havuzda olan ama onların yürütülmesini bekleyen bu görevleri "iptal" bir yol istiyorum.
Ne yazık ki, AsyncCalls.pas bir iş parçacığı havuzuna eklendikten sonra bir görevi iptal etmek için basit bir yol sağlamaz. IAsyncCall yok. İptal veya IAsyncCall. DontDoIfNotAlreadyExecuting veya IAsyncCall. Bana aldırma.
Bunun çalışması için olabildiğince az değiştirmeye çalışarak AsyncCalls.pas'ı değiştirmek zorunda kaldım - yani Andy yeni bir sürüm yayınladığında "Görevi iptal et" fikrim için sadece birkaç satır eklemem gerektiğini Çalışma.
İşte yaptım: IAsyncCall bir "yordam İptal" ekledim. İptal yordamı, havuz görevi yürütmeye başlamak üzereyken denetlenen "FCancelled" (eklenen) alanını ayarlar. IAsyncCall'ı biraz değiştirmem gerekiyordu. Bitti (iptal edildiğinde bile bir arama raporunun tamamlanması için) ve TAsyncCall. InternExecuteAsyncCall yordamı (iptal edilmişse çağrıyı yürütmeme).
Kullanabilirsiniz WinMerge Andy'nin orijinal asynccall.pas ve değiştirilmiş sürümüm arasındaki farkları kolayca bulmak için (indirmeye dahil).
Tam kaynak kodunu indirebilir ve keşfedebilirsiniz.
itiraf
FARKINA VARMAK! :)
CancelInvocation yöntemi AsyncCall öğesinin çağrılmasını engeller. AsyncCall zaten işlenmişse, CancelInvocation çağrısının etkisi yoktur ve AsyncCall iptal edilmediğinden İptal işlevi False değerini döndürür.
İptal edildi yöntemi AsyncCall CancelInvocation tarafından iptal edildiğinde True döndürür.
Unutmak yöntemi, iç AsyncCall'dan IAsyncCall arabiriminin bağlantısını kaldırır. Bu, IAsyncCall arabirimine son başvuru gittiğinde, eşzamansız çağrının yine de yürütüleceği anlamına gelir. Unget çağrıldıktan sonra çağrılırsa arabirimin yöntemleri bir istisna atar. Asenkron işlevi, ana iş parçacığını çağırmamalıdır çünkü TThread'den sonra çalıştırılabilir. Senkronizasyon / Kuyruk mekanizması RTL tarafından kapatıldı ve bu da kilitlenmeye neden olabilir.
Yine de, tüm asenkron çağrıların "asyncHelper ile bitmesini beklemeniz gerekiyorsa yine de AsyncCallsHelper'ımdan yararlanabileceğinizi unutmayın. WaitAll "; veya "CancelAll" seçeneğine ihtiyacınız varsa.