Выполнение запросов в параллельных потоках

Существуют ситуации, при которых несколько запросов могут быть выполнены параллельно. Это позволит сократить общее время выполнения операции до времени выполнения самого долго выполняющегося запроса и, как следствие, длительность возможных блокировок.

Сервер позволяет запуск потоков внутри одного серверного вызова. При этом необходимо самостоятельно контролировать завершение потоков. В каждом созданном потоке будет использовано отдельное соединение с СУБД. Необходимо учитывать, что изменения, сделанные ранее в серверном вызове, не будут видны из этого нового соединения.

У класса ServerCall (из пространства имен Ultima.Server.SessionMgmt) есть метод RunParallel, который принимает массив объектов типа Action или Function, каждый из которых будет запущен в отдельном потоке. Метод сам проконтролирует завершение всех потоков.

Поток можно создать любым другим удобным способом, при этом необходимо самостоятельно создать объект класса ServerCall:

Task.Factory.StartNew(() =>

{

 using (new ServerCall())

 {

         // Выполнение операции.

 }

});

ServerCall.RunParallel запускает задачи в параллельных потоках только если ServerCall.AllowRunParallel = true.

У класса ServerCall существует четыре метода RunParallel:

RunParallel(IEnumerable<Action> actions) – выполняет массив объектов типа Action в параллельных потоках:

actions – массив объектов типа Action для выполнения;

RunParallel<T>(IEnumerable<T> items, Action<T> action) – выполняет действие (объект типа Action) над массивом элементов в параллельных потоках;

T – тип элемента;

items – массив элементов;

action – объект типа Action для выполнения на каждом из элементов;

IEnumerable<R> RunParallel<R>(IEnumerable<Func<R>> functions) – выполняет массив объектов типа Function в параллельных потоках:

R – тип результата;

functions – массив объектов типа Function для выполнения;

IEnumerable<R> RunParallel<T, R>(IEnumerable<T> items, Func<T, R> func) – выполняет действие (объект типа Function) над массивом элементов в параллельных потоках;

T – тип элемента;

R – тип результата;

items – массив элементов;

func – объект типа Function для выполнения на каждом из элементов.

35_important

Все сервисы (специальные менеджеры, а также сервисы ядра и прикладные сервисы) не потокобезопасны. Если на сервере используется многопоточное программирование, каждый поток должен обращаться к своим собственным экземплярам сервисов:

[Import]

private IDictionaryManager DictionaryManager { getset; }

 

private void BadCode(IDList users)

{

 // Такой код писать недопустимо, потому что несколько

 // потоков обращаются к одному экземпляру DictionaryManager

 ServerCall.RunParallel(users, id =>

 {

         var user = DictionaryManager.GetRecord<User>(id);

         user.Name += " (Уволен)";

         DictionaryManager.SaveRecord(user);

 });

}

Вместо этого следует импортировать менеджер следующим образом:

[Import]

private ServerCallImport<IDictionaryManager> DictionaryManagerImport { getset; }

 

private IDictionaryManager DictionaryManager

{

 get { return DictionaryManagerImport.Value; }

}

35_important

Многопоточное программирование на сервере относится к возможностям, пользоваться которыми следует только при крайней необходимости. Ошибку, связанную с многопоточным выполнением, сделать слишком легко, а найти чрезвычайно сложно. Потенциальная выгода от использования многопоточности (скажем, оптимизация производительности выполнения команды) часто несоизмерима с ценой ошибки (некорректная работа команды после оптимизации).