Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
Результаты приведены ниже:
Client thread =16; PoolThread = False
Client thread: count = 0
Server (Sum method) thread =25; PoolThread = True
Client thread: count = 1
Client thread: count = 2
Client thread: count = 3
Client thread: count = 4
Client thread: count = 5
Server (MultBy2 method) thread =27; PoolThread = True
Client thread: count = 6
Client thread: count = 7
Client thread: count = 8
Client thread: count = 9
Client thread: count = 10
SumCallback: Sum = 7
Client thread: count = 11
Client thread: count = 12
Client thread: count = 13
Client thread: count = 14
Client thread: count = 15
MultCallback: MultBy2 = 10
Bye!
Обсуждение
• Видно, что клиентский поток не является потоком из пула рабочих потоков.
• Серверные методы (Sum и MuitBy2) выполняются асинхронно в различных рабочих потоках.
• Во время выполнения асинхронного вызова (от его инициирования до вызова соответствующей callback функции должно пройти 1000 mс) клиент успевает вывести на консоль 10 строк с очередными значениями переменной count.
• Завершается основной поток по завершении последнего асинхронного вызова. Это обеспечивается за счет подсчета числа завершенных вызовов. Заметим, что рабочий поток не может пережить основной поток. Если бы основной поток не контролировал завершение рабочих потоков и завершился бы раньше, то не завершенные рабочие потоки были бы уничтожены.
• Выполнение второго асинхронного вызова начинается с некоторой задержкой. Это связано с тем, что при наличии непустой очереди работ новый рабочий поток формируется через 500 mс после возникновения необходимости в нем. Ранее созданный поток уничтожается, если он никому не понадобился в течении 30 с. Общее число потоков в пуле не должно превышать 25 (на один процесс).
Немного про контекст вызова
Понятие логического вызова связано с цепочкой вызовов, инициирующих друг друга. Эта цепочка может пересекать границы между контекстами, доменами приложений, процессами и машинами. Все вызовы в этой цепочке связаны одним идентификатором — идентификатором логического вызова, что позволяет системе отличать вызовы, относящиеся к различным логическим цепочкам.
С каждым вызовом обычно связана передача некоторых входных и выходных параметров и возвращаемого значения. Кроме того, с вызовом можно связать дополнительный набор свойств, передаваемый от вызывающей стороне вызываемой стороне и обратно — от вызываемой стороны вызывающей стороне. Этот набор свойств называется контекстом вызова. Использование контекста вызова позволяет клиенту и серверу обмениваться данными, не объявленными явно в сигнатуре вызываемого метода. При определенных условиях контекст вызова может пересекать границы между контекстами, доменами приложений, процессами и машинами, сопровождая логический вызов. На каждом этапе можно добавлять в контекст вызова новые свойства, получать их значения и/или удалять старые свойства.
Свойство контекста вызова состоит из пары (имя свойства, значение свойства). Имя свойства должно иметь тип System.String, а в качестве его значения можно задать ссылку на любой объект (производный от System.Object). Если предполагается передача контекста вызова через границу контекста, домена приложения, процесса, машины, значение каждого передаваемого свойства должно быть экземпляром класса, определенного с атрибутом Serializable и производного от ILogicaiThreadAffinative. Определение интерфейса ILogicalThreadAffinative не содержит никаких методов и данный интерфейс используется просто как маркер классов, допускаемых для передачи в контексте вызова за пределы контекста, домена приложения и т. п.
Реально контекст вызова передается через ранее упомянутые границы в форме сообщения типа IMessage. В связи с этим и требуется сериализуемость всех передаваемых объектов (т. е. возможность их передачи по значению).
Ниже представлен пример, демонстрирующий применение контекста вызова, пересекающего границу между процессами. За основу взят многократно рассмотренный пример, связанный с перечислением 5 условных единиц со стороны клиента на счет, поддерживаемый сервером.
Рассмотрим прежде всего код сервера.
Сервер
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Threading;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
namespace MyServer {
[Serializable]
public class MyCallContextUserName:
ILogicalThreadAffinative {
private String _userName;
public MyCallContextUserName {
_userName = Environment.UserName;
}
public String UserName {
get { return _userName; }
}
}
[Serializable]
public class MyCallContextServerName:
ILogicalThreadAffinative {
private Assembly _assembly;
private String _serverName;
public MyCallContextServerName {
_assembly = Assembly.GetExecutingAssembly;
_serverName = _assembly.FullName;
}
public String ServerName {
get { return _serverName; }
}
}
public interface IAccumulator {
void Add(int sum);
}
public interface IAudit {
int Total;
}
[Synchronization]
public class Account: ContextBoundObject,
IAccumulator, IAudit{
protected int sum = 0;