В моей программе .NET я хочу подсчитать количество ударов фрагмента кода. Чтобы сделать его более сложным, мой код обычно выполняется в нескольких потоках, и я не могу контролировать создание/уничтожение потоков (и не знаю, когда они созданы) ... их можно даже объединить. Скажи:Подсчет материалов в нескольких потоках
class Program
{
static int counter = 0;
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Parallel.For(0, 100000000, (a) =>
{
Interlocked.Increment(ref counter);
});
Console.WriteLine(sw.Elapsed.ToString());
}
}
Как счетчик производительности и метода достаточно несколько раз ударил, я хотел бы использовать «нормальные» переменный, в отличии от атомарных/сблокирован целого. Моя вторая попытка состояла в том, чтобы использовать threadlocal storage в сочетании с IDisposable для ускорения работы. Потому что я не могу контролировать создание/уничтожение, я должен следить за переменными хранения:
class Program
{
static int counter = 0;
// I don't know when threads are created/joined, which is why I need this:
static List<WeakReference<ThreadLocalValue>> allStorage =
new List<WeakReference<ThreadLocalValue>>();
// The performance counter
[ThreadStatic]
static ThreadLocalValue local;
class ThreadLocalValue : IDisposable
{
public ThreadLocalValue()
{
lock (allStorage)
{
allStorage.Add(new WeakReference<ThreadLocalValue>(this));
}
}
public int ctr = 0;
public void Dispose()
{
// Atomic add and exchange
int tmp = Interlocked.Exchange(ref ctr, 0); // atomic set to 0-with-read
Interlocked.Add(ref Program.counter, tmp); // atomic add
}
~ThreadLocalValue()
{
// Make sure it's merged.
Dispose();
}
}
// Create-or-increment
static void LocalInc()
{
if (local == null) { local = new ThreadLocalValue(); }
++local.ctr;
}
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Parallel.For(0, 100000000, (a) =>
{
LocalInc();
});
lock (allStorage)
{
foreach (var item in allStorage)
{
ThreadLocalValue target;
if (item.TryGetTarget(out target))
{
target.Dispose();
}
}
}
Console.WriteLine(sw.Elapsed.ToString());
Console.WriteLine(counter);
Console.ReadLine();
}
}
Мой вопрос: можем ли мы сделать это быстрее и/или симпатичнее?
Знаете ли вы, что доступ к полю 'ThreadStatic' будет намного медленнее, чем общий атомный приращение. Вы делаете это хуже. Вы сталкиваетесь с проблемами производительности? –
@SriramSakthivel Запустите тесты, пожалуйста. Исходная версия с 'Interlocked.Increment' выполняется за 1,6 секунды, а вторая версия - 0,2 секунды. – atlaste
@atlaste Вы были правы ... Существует еще одна потенциальная ошибка: 'Dispose()' может вызываться дважды: 'GC' запускается после конца потока, см., Что' ThreadLocalValue' имеет финализатор и помещает его в очередь завершения. TLV не GCed в этот момент. Очередь финализатора работает в другом потоке и решает запустить. 'Dispose()' выполняется в потоке финализатора, в то время как «основной» поток загружает WF для TLV, преуспевая, потому что TLV не был собран, поэтому «основной» поток выполняет «Dispose()». – xanatos