2016-09-06 15 views
8

Bazı "FreeOnTerminate" çalışan iş parçacığım var ve bunlar, yürütme bittiğinde yürütme ve kaldırma işleminden sonra, bir TThreadList tanıtıcıları ekliyor. Ayrıca, çalışmalarını iptal etmeleri için onları uyabilecek bir global etkinlik nesnesini de kontrol ederler.Engelleme çağrısı sırasında bir kilit açma gerektiren bir durum nasıl ele alınır?

Ana parçacığı çalıştıran, olayı bildiren ve olası çalışan iş parçacıklarının sona ermesini bekleyen kısım aşağıdadır. WorkerHandleList, global ThreadList’dür.

... 

procedure WaitForWorkers; 
var 
    ThreadHandleList: TList; 
begin 
    ThreadHandleList := TWorkerThread.WorkerHandleList.LockList; 
    TWorkerThread.WorkerHandleList.UnlockList; 
    WaitForMultipleObjects(ThreadHandleList.Count, 
     PWOHandleArray(ThreadHandleList.List), True, INFINITE); 
end; 

initialization 
    TWorkerThread.RecallAllWorkers := TEvent.Create; 
    TWorkerThread.WorkerHandleList := TThreadList.Create; 

finalization 
    TWorkerThread.RecallAllWorkers.SetEvent; 
    WaitForWorkers; 

    TWorkerThread.RecallAllWorkers.Free; 
    TWorkerThread.WorkerHandleList.Free; 


Bu tasarım, bence, ben ipler kendilerini aynı listeden kendi kolları kaldırmak beri bir kilitlenmeye neden olur çünkü sadece lifler kulpları bekleyen önce listeyi açmak zorunda bir kusur vardır . Herhangi bir kilit olmadan, bir içerik anahtarı, bir iş parçacığının kendisini serbest bırakmasına neden olabilir ve WAIT_FAILED ile hemen geri dönmek için WaitForMultipleObjects neden olur. WaitForMultipleObjects engellediğinden ve kilitlemeyi ana iş parçacığından çıkaramayacağından başka bir kilit kullanamıyorum.

Bu tasarım, FreeOnTerminate iş parçacığı kullanılmaması da dahil olmak üzere çeşitli şekillerde değiştirebilir, bunlar, serbest bırakılana kadar geçerli tanıtıcıları garanti eder. Veya iş parçacığı tutamaçlarının listesini yalnızca ana iş parçacığından değiştirerek. Ya da muhtemelen başkaları ...

Ama sormak istediğim, bu tür bir problemi, tasarımını değiştirmeden bir çözüm var mı? Örneğin, iş parçacığı iş parçacığının kodlarını listeden kaldırmadan önce mi yoksa SwitchToThread numaralı telefonu arayarak çalışan olmayan tüm iş parçacıklarının çalıştırılmasına neden olur mu? Yeterince koşmak mı?

procedure WaitForWorkers; 
var 
    ThreadHandleList: TList; 
    iItemCount : Integer; 
begin 
    repeat 
    ThreadHandleList := TWorkerThread.WorkerHandleList.LockList; 
    try 
     iItemCount := ThreadHandleList.Count 
    finally 
     TWorkerThread.WorkerHandleList.UnlockList; 
    end; 
    if iItemCount = 0 then 
     BREAK; 
    sleep(Whatever_is_suitable); 
    until False; 
end; 

herhangi cpu döngülerinden olursa veya a, sorunu çözmek için basit yolu bu gibi olurdu (sadece ana iş parçacığı diğer yanında, ikincil olanları başlayacağı O) şeylerden kaç varsayarsak

+0

bir [related soru] (http://stackoverflow.com/questions/8252804/when-to-free-a-thread-manually/8254173#8254173) (ı için ifadeleri gerekir ki Buradasınız). – NGLN

cevap

10

LockList() kullanımınız yanlış ve tehlikelidir. UnlockList() numaralı telefonu aradığınızda, TList artık korunmuyor ve çalışan iş parçacıkları kendilerini listeden silerken değiştirilecek. Bu, WaitForMultipleObjects() numaralı telefonu aramadan önce veya WHILE çağrı yığmasını ayarlama şansınız olmadan önce olabilir.

Bunun yerine yapmanız gerekenler listeyi kilitlemek, tutamaçlarını yerel bir diziye kopyalamak, listenin kilidini açmak ve dizinde beklemek. Doğrudan TList üzerinde beklemeyin. Bununla birlikte, bunun bir yarış durumu olmasına rağmen, bununla birlikte. Çalışan iş parçacıklarının bir kısmı, WaitForMultipleObjects()'un gerçekten girilmesinden önce, sonlandırılmış olabilir ve bu nedenle kolları tahrip olmuş olabilir. Ve kalan iş parçacığı kolları WHILE çalışıyor. Her iki şekilde de başarısız olur. Aktif olarak beklerken iş parçacığı kollarını yok edemezsiniz.

FreeOnTerminate=True yalnızca başlattığınız iş parçacıkları için güvenli bir şekilde kullanılabilir ve unutmayın. Hala için konuları erişmeniz gerektiğinde FreeOnTerminate=True kullanımı çok tehlikelidir herhangi bir neden (özellikle çünkü TThread.WaitFor() zaman FreeOnTerminate=True çökmesine eğiliminde olduğunu bu uyarı taşımaktadır - bu iken iplik kolu ve hatta TThread nesne kendisi yok edildi hala kullanılıyor!).

Bekleme stratejinizi yeniden düşünmeniz gerekir. Birkaç alternatifler düşünebilirsiniz:

  1. hiç WaitForMultipleObjects() kullanmayın.

    procedure WaitForWorkers; 
    var 
        ThreadHandleList: TList; 
    begin 
        repeat 
        ThreadHandleList := TWorkerThread.WorkerHandleList.LockList; 
        try 
         if ThreadHandleList.Count = 0 then Exit; 
        finally 
         TWorkerThread.WorkerHandleList.UnlockList; 
        end; 
        Sleep(500); 
        until False; 
    end; 
    
  2. tamamen WorkerHandleList kurtulmak ve bir semafor kullanın veya takip etmek yerine sayacı kenetlenmiş alın: boş olup olmadığını Bu sadece periyodik listesini yeniden kilitlemek ve kontrol etmek, daha güvenli, ancak daha az verimlidir Henüz kaç tane iplik oluşturuldu ve henüz yok edilmedi. Semafor/sayaç daha fazla iş parçacığı olmadığını gösterdiğinde beklemeden çıkın. Ken B gibi

  3. WorkerHandleList kullanmaya devam ama ilk iplik listesinde (değil Execute() yılında, iplik kurucudaki bunu) eklendiğinde reset alır manuel sıfırlama olayı bekleyin önerdi ve sinyal alınca son iş parçacığı listeden kaldırılır (iş parçacığında, Execute() veya DoTerminate() içinde değil). İşte

+0

Teşekkürler. Tutamaçları kopyalamanın ne anlama geldiğini anlıyorum. Ve beklemeden önce olası iş parçacığı sonlandırma, ki bu konudaki kaygımdı. Ancak, onları beklerken iş parçacığı kollarını yok edemediğimi anlamıyorum. Bir iş parçacığı beklemek değil mi? İplik tutamaçlarını ilettiğinizde "WaitForMultipleObjects" başka nasıl geri dönebilir? –

+0

Bir * açık * iplik tutamacında bekleyebilirsiniz ve iş parçacığı OS katmanında tamamen sonlandırıldığında sinyal verilecektir, böylece beklemeyi karşılar. Ancak bu bekleyiş bitene kadar sapın açık ve geçerli kalması gerekir, * SONRA * bunu yok edebilirsiniz. Eğer * işlemden geçirirseniz * iş parçacığı * önce/while * beklenir, özellikle aynı anda diğer açık kollarda beklerseniz, bekleme başarısız olur ve hata size söyleyemeyecektir * hangi * tutamaç hataya neden oldu, böylece diziden kaldırabilir ve kalan tutamaçlarda beklemeye devam edebilirsiniz. –

+0

Tamam, teşekkürler. 'FreeOnTerminate' bir bekletme işleviyle bir seçenek değil, daha önce söylediğin gibi :). –

5

gerekenden daha uzun süre beklemek kabul edilemez, bir Etkinlik oluşturabilir ve bunun yerine bekleyebilir ve tüm iş parçacığı listeden kendilerini aynı işlevden kaldırabilirsiniz. Bu durumda

procedure WaitForWorkers; 
begin 
    Event.WaitFor(INFINITE); 
end; 

procedure RemoveHandleFromList(AHandle : THandle); 
var 
    ThreadHandleList: TList; 
    idx : Integer; 
begin 
    ThreadHandleList := TWorkerThread.WorkerHandleList.LockList; 
    try 
    idx := ThreadHandleList.IndexOf(Pointer(AHandle)); 
    if idx >= 0 then 
    begin 
     ThreadHandleList.Delete(idx); 
     if ThreadHandleList.Count = 0 then 
     Event.SetEvent; 
    end; 
    finally 
    TWorkerThread.WorkerHandleList.UnlockList; 
    end; 
end; 

, muhtemelen manuel sıfırlama olayını kullanmak ve bir "AddHandleToList" prosedüründe sıfırlamak isterdim.

İlgili konular