С# Threading - lock - как проверить блокировки

Im использует фиксирующий объект в моем приложении с несколькими потоками.

Как я могу проверить, сколько раз другие потоки пытались работать с заблокированным объектом, или сколько времени я потратил на попытку обновления заблокированного объекта?

Мой код основан на наилучшем ответе здесь:

Mutliple threads update array

Изменить: код скопирован:

float[] bestResult;
object sync = new Object();
lock (sync) 
{
 if (bestResult[0] > calculatedData[0]) {
 bestResult = calculatedData;
 }
}
4 ответа

Класс System.Diagnostics.Stopwatch может помочь вам:

float[] bestResult;
object sync = new Object();
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
lock (sync) 
{
 sw.Stop();
 if (bestResult[0] > calculatedData[0]) {
 bestResult = calculatedData;
 }
}
Console.WriteLine("Time spent waiting: " + sw.Elapsed);


Хорошим инструментом для начала расследования блокировки является Concurrency Visualizer, как описано здесь. Не бойтесь этой статьи в блоге с 2010 года. Эти же принципы все еще применяются в новых версиях Concurrency Visualizer для более новой версии Visual Studio.


Заданный вопрос заключается в том, как определить количество попыток блокировки или количество времени, потраченного впустую из-за конфликта блокировок.

Ответ на заданный вопрос заключается в использовании профилировщика, такого как тот, который поставляется с Visual Studio Premium Edition. Другие профили .NET могут существовать.

Неправильно добавлять код подсчета/выбора времени в каждом операторе блокировки, поскольку он будет иметь свои собственные проблемы с блокировкой. Поэтому в отсутствие профилировщика вы должны выполнить статический анализ. Это не так страшно. Вложенные петли - большой ключ.

Конфликт блокировок становится самым крупным в дизайне сервера. К счастью, состояние сеанса пользователя является приватным для сеанса. Если вы используете APM (асинхронная модель программирования - обратные вызовы), то при условии, что вы не вызываете socket.BeginRead до конца вашего обработчика, с точки зрения сеанса операции с состоянием эффективно однопоточные. Поэтому в этих условиях блокировка необходима только для установки и срыва сеанса. В течение сеанса это совершенно не нужно.

Вот почему я предпочитаю APM более новые и более модные способы обработки параллельного выполнения.


Я не эксперт по потокам, но для того, чтобы количество раз, когда другие потоки пытались работать над объектом, вам, вероятно, придется реализовать более примитивную версию механизма блокировки, чем Lock. Я дал ему снимок с плотно зацикленным Monitor.TryEnter и комментарии приветствуются.

Конечно, реализация чего-то подобного сама по себе может легко привести к увеличению времени блокировки и большего количества блоков, чтобы как получить нужные вам счеты, так и на основе того факта, что эта реализация, безусловно, не идентична тому, как блокировка работает внутри. В любом случае я потратил время, поэтому я собираюсь опубликовать его.

class Program
 {
 static object lockObj = new object();
 static void Main(string[] args)
 {
 System.Threading.Thread t = new System.Threading.Thread(proc);
 System.Threading.Thread t2 = new System.Threading.Thread(proc);
 t.Start();
 t2.Start();
 t.Join();
 t2.Join();
 Console.WriteLine("Total locked up time = " + (LockWithCount.TotalWaitTicks / 10000) + "ms");
 Console.WriteLine("Total blocks = " + LockWithCount.TotalBlocks);
 Console.ReadLine();
 }
 static void proc()
 {
 for (int x = 0; x < 100; x++)
 {
 using (new LockWithCount(lockObj))
 {
 System.Threading.Thread.Sleep(10);
 }
 }
 }
 }

Выше показано, как использовать LockWithCount, заменив существующий Lock() {} на using(new LockWithCount(x)) {}

class LockWithCount : IDisposable
{
 static System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
 object lockObj;
 public static long TotalWaitTicks = 0;
 public static long TotalBlocks = 0;
 static LockWithCount()
 {
 watch.Start();
 }
 public LockWithCount(object obj)
 {
 lockObj = obj;
 long startTicks = watch.ElapsedTicks;
 if (!System.Threading.Monitor.TryEnter(lockObj))
 {
 System.Threading.Interlocked.Increment(ref TotalBlocks);
 System.Threading.Monitor.Enter(lockObj);
 System.Threading.Interlocked.Add(ref TotalWaitTicks, watch.ElapsedTicks - startTicks);
 }
 }
 public void Dispose()
 {
 System.Threading.Monitor.Exit(lockObj);
 }
}

licensed under cc by-sa 3.0 with attribution.