Отредактировал код, чтобы сделать его потокобезопасным оставлять комментарии
Пожалуйста, посмотрите обновленный вопрос в конце.
Не могли бы вы помочь мне понять, является ли этот код потокобезопасным или как его можно сделать потокобезопасным?
Настройка
В моей системе есть очень простой класс WorkItem.
public class WorkItem
{
public int Id {get;set;}
public string Name {get;set;}
public DateTime DateCreated {get;set;}
public IList<object> CalculatedValues {get;set;}
}
Существует интерфейс ICalculator, который имеет метод, который принимает рабочий элемент, выполняет вычисление и возвращает значение true.
public interface ICalculator
{
bool Calculate(WorkItem WorkItem);
}
Допустим, у нас есть две реализации ICalculator.
public class BasicCalculator: ICalculator
{
public bool Calculate(WorkItem WorkItem)
{
//calculate some value on the WorkItem and populate CalculatedValues property
return true;
}
}
Еще один калькулятор:
public class AnotherCalculator: ICalculator
{
public bool Calculate(WorkItem WorkItem)
{
//calculate some value on the WorkItem and populate CalculatedValues property
//some complex calculation on work item
if (somevalue==0) return false;
return true;
}
}
Существует класс обработчика калькулятора. В его обязанности входит выполнение калькуляторов последовательно.
public class CalculatorHandler
{
public bool ExecuteAllCalculators(WorkItem task, ICalculator[] calculators)
{
bool final = true;
//call all calculators in a loop
foreach(var calculator in calculators)
{
var calculatedValue = calculator.Calculate(WorkItem);
final = final && calculatedValue;
}
return final;
}
}
Наконец, в моем клиентском классе я ввожу ICalculators [], которые важны для прогона. Затем я создаю экземпляр метода ExecuteCalculators ().
Теперь у меня есть большое количество рабочих элементов, и я хочу выполнить для них вычисления, поэтому я создаю список задач, где каждая задача отвечает за создание экземпляра экземпляра CalculatorHandler, а затем берет рабочий элемент и выполняет вычисления, выполняя WaitAll. () для всех задач, например
public class Client
{
private ICalculators[] _myCalculators;
public Client(ICalculators[] calculators)
{
_myCalculators = calculators;
}
public void ExecuteCalculators()
{
var list = new List<Task>();
for(int i =0; i <10;i++)
{
Task task = new Task(() =>
var handler = new CalculatorHandler();
var WorkItem = new WorkItem(){
Id=i,
Name="TestTask",
DateCreated=DateTime.Now
};
var result = handler.ExecuteAllCalculators(WorkItem, _myCalculators);
);
list.Add(task);
}
Task.WaitAll(list);
}
}
Это упрощенная версия системы. Актуальная система имеет ряд калькуляторов, калькуляторы и CalculatorHandler вводятся через IoC и т. Д.
Мои вопросы - помогите мне понять эти моменты:
Каждая задача создает новый экземпляр CalculatorHandler. Является ли это
означает, что все, что происходит в CalculatorHandler, является потокобезопасным, так как
не имеет общедоступных свойств и просто зацикливается
калькуляторы
Калькуляторы используются всеми задачами, поскольку они являются переменными-членами класса Client, но передаются в
CalculatorHandler, который создается для каждой задачи. Означает ли это, что когда все задачи запускаются, как новые
экземпляр CalculatorHandler создан, поэтому калькуляторы
автоматически безопасен для потоков, и мы не столкнемся с проблемами потоков, например, тупики и т.д?
Подскажите, пожалуйста, как я могу сделать код безопасным? Это
лучше всего передать Func <'ICalculators>' [] в класс Client, а затем в каждой задаче мы можем выполнить Func <'ICalculator'> () и затем передать эти экземпляры в ICalculator? Func <'ICalculator'> вернет экземпляр ICalculator.
Правда ли, что калькуляторы передаются как переменная частного метода, поэтому другие экземпляры CalulatorHandler не могут запускать один и тот же экземпляр калькулятора? Или, поскольку калькуляторы являются ссылочными типами, мы неизбежно столкнемся с многопоточностью?
Обновление
Не могли бы вы помочь мне понять, является ли этот обновленный код поточно-ориентированным или как его можно сделать поточно-безопасным?
Настройка
В моей системе очень простой класс WorkItem. Он имеет общедоступные свойства геттера, кроме 1 свойства "CalculatedValues".
public class WorkItem
{
public int Id {get;}
public string Name {get;}
public DateTime DateCreated {get;}
public IList<object> CalculatedValues {get;set;}
public WorkItem(int id, string name, DateTime dateCreated)
{
Id = id,
Name = name,
DateCreated = dateCreated
}
}
Существует интерфейс ICalculator, который имеет метод, который принимает рабочий элемент, выполняет вычисления и возвращает IList. Это не меняет состояние рабочего элемента.
public interface ICalculator
{
IList<object> Calculate(WorkItem WorkItem);
}
Допустим, у нас есть две реализации ICalculator.
public class BasicCalculator: ICalculator
{
public IList<object>Calculate(WorkItem WorkItem)
{
//calculate some value and return List<object>
return List<object>{"A", 1};
}
}
Еще один калькулятор:
public class AnotherCalculator: ICalculator
{
public bool Calculate(WorkItem WorkItem)
{
//calculate some value and return List<object>
return List<object>{"A", 1, workItem.Name};
}
}
Есть класс обработчика калькулятора. В его обязанности входит выполнение калькуляторов последовательно. Обратите внимание, что он принимает ICalculator в своем конструкторе, когда он создается. Он также имеет частный объект статической блокировки при обновлении экземпляра рабочего элемента.
public class CalculatorHandler
{
private ICalculators[] _calculators;
public CalculatorHandler(ICalculators[] calculators)
{
_calculators = calculators;
}
//static lock
private static object _lock = new object();
public bool ExecuteAllCalculators(WorkItem workItem, ICalculator[] calculators)
{
bool final = true;
//call all calculators in a loop
foreach(var calculator in calculators)
{
var calculatedValues = calculator.Calculate(workItem);
//within a lock, work item is updated
lock(_lock)
{
workItem.CalculatedValues = calculatedValues;
}
}
return final;
}
}
Наконец, в своем клиентском классе я выполняю CalculatorHandler.
Теперь у меня есть большое количество рабочих элементов, и я хочу выполнить для них вычисления, поэтому я создаю список задач, где каждая задача отвечает за создание экземпляра экземпляра CalculatorHandler, а затем берет рабочий элемент и выполняет вычисления, выполняя WaitAll. () для всех задач, например
public class Client
{
public void ExecuteCalculators()
{
var list = new List<Task>();
for(int i =0; i <10;i++)
{
Task task = new Task(() =>
//new handler instance and new calculator instances
var handler = new CalculatorHandler(new[]{
new BasicCalculator(), new AnotherCalculator()
});
var WorkItem = new WorkItem(
i,
"TestTask",
DateTime.Now
};
var result = handler.ExecuteAllCalculators(WorkItem);
);
list.Add(task);
}
Task.WaitAll(list);
}
}
Это упрощенная версия системы. Актуальная система имеет ряд калькуляторов, калькуляторы и CalculatorHandler вводятся через IoC и т. Д.
Мои вопросы - помогите мне понять эти моменты:
Каждая задача создает новый экземпляр CalculatorHandler и новые экземпляры ICalculators. Калькуляторы не выполняют никаких операций ввода-вывода, а только создают новый частный IList. Являются ли обработчики калькулятора и экземпляры калькулятора потокобезопасными?
CalculatorHandler обновляет рабочий элемент, но в пределах блокировки. Блокировка - это статический закрытый объект. Означает ли это, что все экземпляры CalculatorHandler будут использовать одну и ту же блокировку, и поэтому в один момент только один поток может обновить рабочий элемент?
Рабочий элемент имеет все общедоступные свойства получателя, кроме его свойства CalculatedValues. CalculatedValues устанавливается только в статической блокировке. Этот код теперь потокобезопасен?