#c# #multithreading
#c# #многопоточность
Вопрос:
У меня есть класс, который я хочу использовать в разных сценариях, один из которых находится в однопоточном env, а другой — в многопоточном. Мне интересно, могу ли я с помощью generics или какой-либо другой парадигмы создать класс, который реализует эти два поведения, без необходимости создавать два отдельных класса или передавать флаг, который проверяется в каждом методе.
2 класса, 1 потокобезопасный, 1 нетопоточный. Могу ли я элегантно объединить в один?
public class Position
{
public void Adjust(Trade newfill)
{
lock(locker)
{
....
}
}
}
public class PositionNTS
{
public void Adjust(Trade newfill)
{
...
}
}
Комментарии:
1. Неоспоримая блокировка займет, возможно, 20 наносекунд. Если этот код не очень чувствителен к производительности, тогда просто используйте код с блокировкой, даже в однопоточной среде.
2. Если вам действительно нужна разница: создайте
interface
и внедрите любой из классов, где они необходимы. Ваша фабрика должна решить, какой тип обработки здесь необходим, и затем предоставить надлежащую реализацию.3. Дополнительные затраты на виртуальную отправку (плохое статическое предсказание) вполне могут превысить затраты на ввод неоспоримой блокировки.
Ответ №1:
Будет ли интерфейс работать для вашего?
public interface IPosition
{
void Adjust(Trade newfill)
}
public class PositionNTS : IPosition
{
public void Adjust(Trade newfill)
{
...
}
}
public class Position : IPosition
{
private readonly object locker = new object();
public void Adjust(Trade newfill)
{
lock (locker)
{
...
}
}
}
Вы спрашивали об generics, так что, возможно, это то, что вы не хотите использовать (я предполагаю, что есть определенные части вашего кода, которые вы хотите использовать в потокобезопасной части, а другие части, которые вы хотите использовать в не потокобезопасной части, вместо простого способа переключения между ними). Если под средой вы подразумеваете что-то вроде производственной среды или локальной среды (возможно, подойдет не потокобезопасная версия), вы можете использовать IoC для переключения между тем, как будет создан интерфейс. Смотрите ссылку для получения дополнительной информации.
Дополнительная информация о IoC: http://www.ninject.org/learn.html
Ответ №2:
Я склонен согласиться с Джимом.
Но, если вам все еще нужен такой класс, и учитывая, что оба класса предоставляют идентичные функции, но один из классов является потокобезопасным. Тогда почему бы не использовать следующее?
Создайте / используйте экземпляр в соответствии с вашей потоковой средой, из которой вы будете использовать эти классы.
public class Trade
{
}
public class PositionNTS
{
public virtual void Adjust(Trade newfill)
{
}
}
public class Position : PositionNTS
{
private readonly object locker = new object();
public override void Adjust(Trade newfill)
{
lock (locker)
{
base.Adjust(newfill);
}
}
}
Обновить
Это другой подход, который вы можете использовать, решение по-прежнему остается на более высоком уровне, чтобы указать, какой общий параметр должен быть передан.
public class Trade
{
}
public interface IRunner
{
void Adjust(Trade newfill);
}
public class Executor
{
private IRunner runner;
public void Adjust<T>(Trade newFill) where T : class, IRunner, new()
{
if(runner == null || (runner as T) == null)
{
runner = new T();
}
runner.Adjust(newFill);
}
}
public class Runner : IRunner
{
public void Adjust(Trade newfill)
{
//your logic here.
}
}
public class SynchronizedRunner : IRunner
{
private readonly object syncLockObject = new object();
public void Adjust(Trade newfill)
{
lock (syncLockObject)
{
//your logic here.
}
}
}
Комментарии:
1. в зависимости от контекста это может быть рискованно (использование вызовов методов внутри блокировки может легко привести к плохим условиям гонки, еще хуже, конечно, будут события — но кто знает, может быть, Adjust запустит события позже?)
2. Я думаю, что спрашивающий должен знать об этом. И это то, что я упоминал о контексте, в котором используется экземпляр в соответствии с потоковой средой.
3. Я бы предпочел этого не делать, поскольку мне придется вносить какие-либо изменения в оба класса. Я полагаю, что самое безопасное, что можно сделать, это оставить все как есть с блокировкой. У нас есть однопоточный симулятор и многопоточный оптимизатор, которые совместно используют этот класс. Оптимизатор работает довольно медленно, и мы обнаружили много проблем с производительностью, которые мы можем исправить. Мне было интересно, можно ли что-то сделать с этим классом, но это, вероятно, наименее оскорбительная из наших проблем. Спасибо за помощь!
4. Еще один обновленный способ, и да, к сожалению, класс не может неявно решать «использовать или не использовать блокировку».