Eşzamanlı programlama ile ilgili bu kelimeleri duydum, ama aralarındaki fark nedir?
Eşzamanlı programlama ile ilgili bu kelimeleri duydum, ama aralarındaki fark nedir?
Yanıtlar:
Kilit, kilitlenen parçaya yalnızca bir iş parçacığının girmesine izin verir ve kilit başka işlemlerle paylaşılmaz.
Muteks kilitle aynıdır ancak sistem çapında olabilir (birden çok işlemle paylaşılabilir).
Bir semafor bir muteks ile aynı şeyi yapar ancak x iş parçacığının girmesine izin verir, bu örneğin aynı anda çalışan cpu, io veya ram yoğun görevlerin sayısını sınırlamak için kullanılabilir.
Muteks ve semafor arasındaki farklar hakkında daha ayrıntılı bir yazı için burayı okuyun .
Ayrıca, herhangi bir zamanda sınırsız sayıda okuyucuya veya 1 yazara izin veren okuma / yazma kilitleriniz de vardır.
Bu sözlerle ilgili birçok yanlış anlama var.
Bu, buraya mükemmel uyan önceki bir gönderiden ( https://stackoverflow.com/a/24582076/3163691 ):
1) Kritik Bölüm = Bir işlem içinde diğerlerinden yalnızca bir etkin iş parçacığının yürütülmesine izin vermek için kullanılan kullanıcı nesnesi . Seçilmeyen diğer evreler (bu nesneyi elde etmek için) uyku moduna alınır .
[İşlemler arası yetenek yok, çok ilkel bir nesne].
2) Mutex Semaphore (aka Mutex) = Farklı süreçler arasında diğerlerinden sadece bir aktif iş parçacığının yürütülmesine izin vermek için kullanılan çekirdek nesnesi . Seçilmeyen diğer evreler (bu nesneyi elde etmek için) uyku moduna alınır . Bu nesne, iş parçacığı sahipliğini, iş parçacığı sonlandırma bildirimini, özyinelemeyi (aynı iş parçacığından birden çok 'alma' çağrısı) ve 'öncelikli ters çevirme önleme'yi destekler.
[İşlemler arası kapasite, kullanımı çok güvenli, bir tür 'yüksek seviye' senkronizasyon nesnesi].
3) Sayım Semaforu (diğer adıyla Semaphore) = Bir grup aktif iş parçacığının diğerlerinden çalıştırılmasına izin vermek için kullanılan çekirdek nesnesi . Seçilmeyen diğer evreler (bu nesneyi elde etmek için) uyku moduna alınır .
Bununla birlikte, 'muteks' özniteliklerine sahip olmadığı için süreçler arası kullanım çok güvenli değildir: iş parçacığı sonlandırma bildirimi, özyineleme ?, 'öncelikli ters çevirme önleme'? Vb.).
4) Ve şimdi, 'spinlocks' hakkında konuşmak, önce bazı tanımlar:
Kritik Bölge = 2 veya daha fazla işlemle paylaşılan bir bellek bölgesi.
Lock = Değeri 'kritik bölgeye' girişe izin veren veya reddeden bir değişken. (Basit bir 'boole bayrağı' olarak uygulanabilir).
Meşgul bekleme = Bir değer görünene kadar bir değişkenin sürekli olarak test edilmesi.
En sonunda:
Spin-lock (diğer adıyla Spinlock) = Meşgul beklemeyi kullanan bir kilit . ( Kilidin alınması xchg veya benzeri atomik işlemlerle yapılır ).
[İplik uyku yok, çoğunlukla yalnızca çekirdek düzeyinde kullanılır. Kullanıcı seviyesi kodu için yetersiz].
Son bir yorum olarak, emin değilim ama yukarıdaki ilk 3 senkronizasyon nesnesinin (# 1, # 2 ve # 3) bu basit canavarı (# 4) uygulamalarının bir parçası olarak kullandıkları bazı büyük paralara bahse girebilirim.
İyi günler!.
Referanslar:
Li tarafından Caroline Yao (CMP Books) ile Gömülü Sistemler için Gerçek Zamanlı Kavramlar.
-Modern İşletim Sistemleri (3.) Andrew Tanenbaum (Pearson Education International).
-Windows Richter (Microsoft Programlama Serisi) tarafından Microsoft Windows (4.) için Programlama Uygulamaları.
Ayrıca, şuraya da bakabilirsiniz: https://stackoverflow.com/a/24586803/3163691
Çoğu problem (i) sadece kilitler, (ii) sadece semaforlar, ... veya (iii) her ikisinin bir kombinasyonu kullanılarak çözülebilir! Bildiğiniz gibi, çok benzerler: her ikisi de yarış koşullarını önler , her ikisi de acquire()
/ release()
işlemleri vardır, her ikisi de sıfır veya daha fazla iş parçacığının engellenmesine / şüphelenilmesine neden olur ... Gerçekten, önemli fark sadece nasıl kilitlendiklerine ve kilidini açtıklarına dayanır .
Her iki kilit / semafor için, acquire()
ilkel 0 durumundayken çağrı yapmaya çalışmak , çağıran iş parçacığının askıya alınmasına neden olur. Kilitler için - kilit 1 durumundaki edinme girişimleri başarılı. Semaforlar için - {1, 2, 3, ...} durumlarında kilidi alma girişimleri başarılı olur.
Durum durumu 0'daki kilitler için , daha önce çağrılan aynı iş parçacığı varsa acquire()
, şimdi sürüm çağırır, sürüm başarılı olur. Bir Eğer farklı iplik denedim - bu olur (ihmal genellikle girişimi veya bir hata atılır) ne olduğu uygulama / kütüphaneye aşağıda. Durum 0'daki semaforlar için, herhangi bir iş parçacığı bırakmayı çağırabilir ve başarılı olur (daha önce kullanılan hangi iş parçacığının semaforu 0 durumuna getirmek için aldığından bağımsız olarak).
Yukarıdaki tartışma itibaren biz kilitleri bir bir kavram var olduğunu görebilirsiniz sahibine semaforlar (herhangi bir iş parçacığı bir semaforda salınımını çağırabilir) bir sahibi yok oysa, (serbest bırakma sahibidir çağırabilir tek iplik).
Çok fazla karışıklığa neden olan şey, pratikte bu üst düzey tanımın birçok varyasyonu olmasıdır.
Dikkate alınması gereken önemli varyasyonlar :
acquire()
/ release()
Denen ne olmalı ? - [Büyük ölçüde değişir ]Bunlar kitabınıza / okutmanınıza / dilinize / kütüphanenize / ortamınıza bağlıdır.
İşte bazı dillerin bu ayrıntılara nasıl cevap verdiğini belirten hızlı bir tur.
pthread_mutex_t
. Varsayılan olarak, diğer işlemlerle ( PTHREAD_PROCESS_PRIVATE
) paylaşılamazlar , ancak mutekslerin pshared adlı bir özniteliği vardır . Ayarlandığında, muteks işlemler ( PTHREAD_PROCESS_SHARED
) arasında paylaşılır .sem_t
. Mutekslere benzer şekilde, semaforlar birçok işlemin yüzdeleri arasında paylaşılabilir veya tek bir işlemin iş parçacıklarına özel tutulabilir. Bu, sağlanan pshared argümana bağlıdır sem_init
.threading.RLock
) çoğunlukla C / C ++ pthread_mutex_t
s ile aynıdır . Her ikisi de evre . Bu, bunların kilidini yalnızca onu kilitleyen aynı iplikle açabileceği anlamına gelir. O durumda sem_t
semaforlar, threading.Semaphore
semaforlar ve theading.Lock
kilitleri evresel değildir - bu durumda için herhangi semaforda aşağı / kilit açma gerçekleştirebilir.threading.Semaphore
) çoğunlukla ile aynıdır sem_t
. Bununla birlikte sem_t
, bir iş parçacığı kimlikleri kuyruğu, kilitli iken kilitlemeye çalışırken iş parçacıklarının engellenme sırasını hatırlamak için kullanılır. Bir iş parçacığı semaforun kilidini açtığında , kuyruktaki ilk iş parçacığı (varsa) yeni sahip olarak seçilir. İş parçacığı tanımlayıcısı kuyruktan alınır ve semafor yeniden kilitlenir. Bununla birlikte, threading.Semaphore
sıra yerine bir küme kullanılır, böylece evrelerin engellenme sırası saklanmaz - kümedeki herhangi bir evre bir sonraki sahip olarak seçilebilir.java.util.concurrent.ReentrantLock
) çoğunlukla C / C ++ ile aynıdır pthread_mutex_t
ve Python's threading.RLock
da bir yeniden giriş kilidi uygular. JVM'nin aracı görevi görmesi nedeniyle işlemler arasında kilitleri paylaşmak daha zordur. Bir iş parçacığı, sahip olmadığı bir kilidi açmaya çalışırsa, a IllegalMonitorStateException
atılır.java.util.concurrent.Semaphore
) daha çok aynıdır sem_t
ve threading.Semaphore
. Java semaforları için yapıcı , bekleyen iş parçacıklarını saklamak için bir küme (false) veya kuyruk (true) kullanıp kullanılmayacağını denetleyen bir adalet boole parametresini kabul eder . Teorik olarak semaforlar sıklıkla tartışılır, ancak pratikte semaforlar çok fazla kullanılmaz. Bir semafor yalnızca bir tamsayının durumunu tutar , bu nedenle çoğu zaman oldukça esnek değildir ve bir kerede birçoğuna ihtiyaç vardır - kodun anlaşılmasında zorluklara neden olur. Ayrıca, herhangi bir ipliğin bir semaforu serbest bırakabilmesi bazen istenmez. Bunun yerine, "koşul değişkenleri" ve "monitörler" gibi daha nesne yönelimli / üst düzey senkronizasyon ilkelleri / soyutlamaları kullanılır.
John Kopplin'in Multithreading Tutorial'a bir göz atın .
Konular Arasında Senkronizasyon bölümünde olay, kilit, muteks, semafor, bekleme süresi arasındaki farkları açıklar.
Bir muteks , aynı anda yalnızca bir iş parçacığına ait olabilir ve iş parçacıklarının paylaşılan bir kaynağa karşılıklı olarak özel erişimi koordine etmesini sağlar
Kritik bölüm nesneleri , mutex nesneleri tarafından sağlanana benzer senkronizasyon sağlar, ancak kritik bölüm nesneleri yalnızca tek bir işlemin iş parçacıkları tarafından kullanılabilir
Muteks ve kritik bölüm arasındaki diğer bir fark , kritik bölüm nesnesinin şu anda başka bir iş parçacığına ait olması durumunda mutex ile birlikte kullanılan
EnterCriticalSection()
süresiz olarak beklemeyiWaitForSingleObject()
, bir zaman aşımını belirtmenize izin vermesidir.Bir semafor , eşzamanlı olarak paylaşılan bir kaynağa erişen iş parçacığı sayısını sınırlandırarak sıfır ile maksimum değer arasında bir sayı tutar.
Örneklerle örtmeye çalışacağım:
Kilitle: Kullanacağınız örnek, lock
(benzersiz anahtarları olması gereken) öğelerin eklendiği paylaşılan bir sözlük olabilir.
Kilit, bir iş parçacığının sözlükte olan öğeyi kontrol eden kod mekanizmasına girmemesini sağlarken, başka bir iş parçacığı (kritik bölümdeki) bu denetimi geçmiş ve öğeyi ekler. Başka bir iş parçacığı kilitli bir kod girmeye çalışırsa, nesne serbest bırakılana kadar bekler (engellenir).
private static readonly Object obj = new Object();
lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
if (!sharedDict.ContainsKey(key))
{
sharedDict.Add(item);
}
}
Semafor: Diyelim ki bir bağlantı havuzunuz var, o zaman tek bir iş parçacığı semaforun bağlantı kurmasını bekleyerek havuzdaki bir öğeyi ayırabilir. Daha sonra bağlantıyı kullanır ve iş yapıldığında semaforu serbest bırakarak bağlantıyı serbest bırakır.
Sevdiğim kod örneği @Patric tarafından verilen fedai biridir - işte gidiyor:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
MutexSemaphore(1,1)
Küresel olarak oldukça fazla ve sıklıkla kullanılır (aksi takdirde geniş çapta uygulama lock
daha uygundur). Küresel Mutex
olarak erişilebilir bir listeden düğümü silerken global kullanılır ( düğümü silerken başka bir iş parçacığının bir şey yapmasını istediğiniz son şey). Edindiğiniz zaman Mutex
farklı iplik çalışır aynı elde etmek eğer Mutex
o elde ettiği AYNI parçacığı kadar uyumaya konacaktır Mutex
bültenleri o.
Küresel muteks yaratmaya iyi bir örnek @deepee
class SingleGlobalInstance : IDisposable
{
public bool hasHandle = false;
Mutex mutex;
private void InitMutex()
{
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
mutex = new Mutex(false, mutexId);
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
}
public SingleGlobalInstance(int timeOut)
{
InitMutex();
try
{
if(timeOut < 0)
hasHandle = mutex.WaitOne(Timeout.Infinite, false);
else
hasHandle = mutex.WaitOne(timeOut, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
}
catch (AbandonedMutexException)
{
hasHandle = true;
}
}
public void Dispose()
{
if (mutex != null)
{
if (hasHandle)
mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
sonra gibi kullanın:
using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
//Only 1 of these runs at a time
GlobalNodeList.Remove(node)
}
Umarım bu size biraz zaman kazandırır.
Wikipedia, Semaforlar ve Muteksler arasındaki farklar konusunda harika bir bölüme sahiptir :
Muteks, aslında ikili bir semaforla aynı şeydir ve bazen aynı temel uygulamayı kullanır. Aralarındaki farklar:
Muteksler, muteksi kilitleyen süreç olan bir sahip kavramına sahiptir. Sadece muteksi kilitleyen işlem kilidini açabilir. Buna karşılık, bir semaforun sahiplik kavramı yoktur. Herhangi bir işlem semaforun kilidini açabilir.
Semaforların aksine, muteksler öncelikli ters çevirme güvenliği sağlar. Muteks mevcut sahibini bildiğinden, muteks üzerinde daha yüksek öncelikli bir görev beklemeye başladığında sahibin önceliğini yükseltmek mümkündür.
Muteksler ayrıca, muteksi tutan işlemin yanlışlıkla silinemediği silme güvenliği de sağlar. Semaforlar bunu sağlamaz.
Anladığım kadarıyla, bir muteksin yalnızca tek bir işlemde değil, birçok iş parçacığında kullanılabilirken, bir semafor birden fazla işlemde ve karşılık gelen iş parçacığı kümelerinde kullanılabilir.
Ayrıca, bir muteks ikili (kilitli veya kilidi açık) iken, bir semaforun sayma kavramı veya birden fazla kilitleme ve kilit açma isteği kuyruğu vardır.
Birisi açıklamamı doğrulayabilir mi? Linux bağlamında, özellikle 2.6.32 çekirdeğini kullanan Red Hat Enterprise Linux (RHEL) sürüm 6'da konuşuyorum.
Örnek olarak bir Linux varyantında C programlama kullanmak temel bir durumdur.
Kilit:
• Genellikle kilitli veya kilitsiz çalışan çok basit bir ikili yapı
• İş parçacığı sahipliği, öncelik, sıralama vb. Kavramları yok.
• Genellikle ipliğin sürekli olarak kilitlerin kullanılabilirliğini kontrol ettiği bir dönüş kilidi.
• Genellikle atomik işlemlere dayanır, örn. Test ve set, karşılaştırma ve takas, getirme ve ekleme vb.
• Genellikle atomik işlem için donanım desteği gerektirir.
Dosya Kilitleri:
• Genellikle bir dosyaya birden çok işlem aracılığıyla erişimi koordine etmek için kullanılır.
• Birden çok işlem okuma kilidini tutabilir, ancak herhangi bir işlem yazma kilidini tuttuğunda başka hiçbir işlemin okuma veya yazma kilidi edinmesine izin verilmez.
• Örnek: sürü, fcntl vb.
Karşılıklı dışlama:
• Mutex işlev çağrıları genellikle çekirdek alanında çalışır ve sistem çağrılarıyla sonuçlanır.
• Mülkiyet kavramını kullanır. Sadece şu anda muteksi tutan iş parçacığı kilidini açabilir.
• Mutex özyinelemeli değildir (İstisna: PTHREAD_MUTEX_RECURSIVE).
• Genellikle Koşul Değişkenleriyle İlişkili olarak kullanılır ve örneğin pthread_cond_signal, pthread_cond_wait vb.
• Bazı UNIX sistemleri muteks'in birden çok işlem tarafından kullanılmasına izin verir, ancak bu tüm sistemlerde uygulanmayabilir.
Semafor:
• Bu, değerleri sıfırın altına düşmesine izin verilmeyen bir çekirdek tarafından korunan tamsayıdır.
• İşlemleri senkronize etmek için kullanılabilir.
• Semaforun değeri 1'den büyük bir değere ayarlanabilir; bu durumda değer genellikle kullanılabilir kaynak sayısını gösterir.
• Değeri 1 ve 0 ile sınırlanmış bir semafor ikili semafor olarak adlandırılır.
Supporting ownership
, maximum number of processes share lock
ve maximum number of allowed processes/threads in critical section
genel adıyla eşzamanlı nesnenin adını / türünü belirleyen üç ana faktör vardır lock
. Bu faktörlerin değeri ikili olduğu için (iki durumu vardır), bunları 3 * 8 gerçeğe benzer bir tabloda özetleyebiliriz.
X Y Z Name
--- --- --- ------------------------
0 ∞ ∞ Semaphore
0 ∞ 1 Binary Semaphore
0 1 ∞ SemaphoreSlim
0 1 1 Binary SemaphoreSlim(?)
1 ∞ ∞ Recursive-Mutex(?)
1 ∞ 1 Mutex
1 1 ∞ N/A(?)
1 1 1 Lock/Monitor
Bu tabloyu düzenlemek veya genişletmekten çekinmeyin, düzenlenebilir olması için bir ascii tablosu olarak gönderdim :)