29 Apr
2006

Thread-Synchronisation ohne lock()

 

Wenn mehrere Threads auf die gleiche Datenstruktur zugreifen, dann muss man den Zugriff darauf zwischen diesen Threads synchronisieren. Besonders wenn es passieren kann da ein Thread lesend und ein anderer schreibend darauf zugreifen will. Macht man dies nicht ist ein Crash über kurz oder lang vorprogrammiert. Es macht sich nicht gut wenn man z.B. eine Liste ändert während ein anderer sich auf die Struktur verlässt. Dazu wird einem z.B. von C# die lock() Anweisung zu Verfügung gestellt. Damit wird sichergestellt dass alles was innerhalb der lock() Anweisung steht nur von einem Thread parallel ausgeführt werden kann.

object _lock = new object(); 
UserDictionary _userList = new UserDictionary(); 
public void ShowList() { 
	lock(_lock) { 
		foreach (User user in _userList) { 
			[User ausgeben] 
		} 
	} 
} 

public void AddUser(User user) { 
	lock (_lock) { 
		_userList.Add(user); 
	} 
} 

Damit ist gesichert dass während dem Anzeigen keine User hinzugefügt werden können, und somit der Iterator auf die sich foreach() verlässt während der Aktion konsistent ist. Nachteil ist jedoch, dass sich lesende Zugriffe mit lock() auch gegenseitig ausschließen und blockieren, da lock() dies nicht unterscheidet. Ein Fall wäre zum Beispiel ein WebForum was eine Liste der aktuellen Benutzer anlegt und sie auf jeder Seite anzeigen will, die Seite wird oft anzeigt, aber ein neuer kommt seltener hinzu bzw. wieder raus aus der Liste. Das .NET Framework bietet dazu die ReaderWriterLock-Klasse, damit kann eine Synchronisation abhängig vom Zweck realisieren, und somit sich selbst blockierende Lesezugriffe verhindern.

ReaderWriterLock _lock = new ReaderWriterLock(); 
UserDictionary _userList = new UserDictionary(); 

public void ShowUsers() { 
_lock.AcquireReaderLock(0); 
	foreach (User user in _userList) { 
		// [User ausgeben] 
	} 
_lock.ReleaseReaderLock();
} 

public void AddUser(User user) { 
	_lock.AcquireWriterLock(0) 
	_userList.Add(user); 
	_lock.ReleaseWriterLock(); 
}

Damit ist sichergestellt alle Threads die Userliste gleichzeitig auslesen können, solange niemand schreiben darauf zugreift. AquireWriterLock() wartet solange bis keine ReaderLocks mehr bestehen. AquireReaderLock() wartet nur wenn noch auch ein WriterLock besteht. Auch kann man innerhalb eines ReaderLocks den lock mit UpgradeToWriterLock() zu einem WriterLock hochstufen, um dann Daten zu ändern. Eine Hcohstufung des locks muss mit DowngradeFromWriterLock() wieder Rückgängig gemacht werden. Weitere Informationen zu ReaderWriterLock() finden sich dazu natürlich in der MSDN Library [1]


[1] http://msdn2.microsoft.com/System.Threading.ReaderWriterLock


Der Eintrag ist mir etwas Wert
 
Comments have been closed on this topic.