Здравствуйте, большое спасибо всем, кто отвечает!
Вот и я сам добрался до момента, когда не могу разобраться, прошу вашей помощи. Имеется такой код:
в двух словах, приложение подключается к биржевой программе, которая по средствам RTD interface в реальном времени отдает данные о котировках и их обновлении, проблема как раз в обновлении.
Извне вызывается моя функция UpdateNotify() класса RtdUpdate.
Как правильно построить логику передачи управления внутри приложения, при условии вызова одной из функций извне?
В каком месте следует передавать выполнение кода в новый поток при том же условии (вызов одной из функций извне)?
Что стоит учесть при написании функций, которые вызываются извне?
Имеется такой код:
public partial class Form1 : Form
{
//создаем переменную типа RtdClient
RtdClient rtdClient;
//создаем переменную типа updateNotification
RtdUpdate rtdUpdate;
//конструктор Form1
public Form1()
{
InitializeComponent();
var tosClassId = new Guid(Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\myapp.COMApp.1\CLSID\", "", null).ToString());
rtdClient = new RtdClient(tosClassId, this);
//отправляем ссылку на наш клиент, для обновлений
rtdUpdate = new RtdUpdate(ref rtdClient);
//подписываем наш клиент на обновления
rtdClient.StartListenUpdate(ref rtdUpdate);
}
//функция, обновляет значения в массивах, если пришло update
public void UpdateOnePrice(object _inputSendDataToThread)
{
/* тут код который анализирует обновление */
}
//класс реализует RtdClient
public class RtdClient
{
Form1 form;
Type rtd;
IRtdServer server;
public RtdClient(Guid _serverId, Form1 _form)
{
form = _form;
rtd = Type.GetTypeFromCLSID(_serverId);
server = (IRtdServer)Activator.CreateInstance(rtd);
}
public void StartListenUpdate(ref RtdUpdate _rtdUpdate)
{
server.ServerStart(_rtdUpdate);
}
public void updateData()
{
try
{
var refresh = server.RefreshData(1);
if (refresh.Length > 0)
{
sendDataToThread send = new sendDataToThread();
send.data[0, 0] = refresh[0, 0];
send.data[1, 0] = refresh[1, 0];
form.UpdateOnePrice(send);
}
}
catch (Exception ex)
{
// TODO: Log exception
MessageBox.Show("error:" + ex);
}
}
}
public class RtdUpdate : IRTDUpdateEvent
{
RtdClient rtdClient = null;
ThreadManager threadManager;
public delegate void MethodContainer();
public RtdUpdate(ref RtdClient _rtdClient)
{
rtdClient = _rtdClient;
threadManager = new ThreadManager();
}
public void UpdateNotify()
{
//узнаем номер свободного потока
int numThread = -1;
while (numThread < 0)
{
numThread = threadManager.FreeThreadPlz();
}
//запускаем обновление в свободном потоке
threadManager.threads[numThread] = new Thread(new ThreadStart(rtdClient.updateData));
threadManager.threads[numThread].Start();
}
}
//класс для того, чтобы описать, то что передаем в новый поток
public class sendDataToThread
{
public object[,] data = new object[2, 1];
}
//класс который реализует управление потоками
public class ThreadManager
{
//кол-во разрешенных потоков
int threadCount = 10;
//массив с рабочими потоками
public Thread[] threads = new Thread[10];
//какой поток сейчас свободен
public int FreeThread = 0;
//конструктор
public ThreadManager()
{
Array.Resize(ref threads, threadCount);
}
public int FreeThreadPlz()
{
int i = 0;
for (i = 0; i < threadCount; i++)
{
try
{
if (!threads[i].IsAlive)
{
return i;
}
}
catch (Exception)
{
return i;
}
}
/*
LOG TO all BUSY THREAD
*/
return -1;
}
}
//ниже описание интерфейсов
[ComImport, TypeLibType((short)0x1040), Guid("EC0E6191-DB51-11D3-8F3E-00C04F3651B8")]
public interface IRtdServer
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10)]
int ServerStart([In, MarshalAs(UnmanagedType.Interface)] IRTDUpdateEvent callback);
[return: MarshalAs(UnmanagedType.Struct)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
object ConnectData([In] int topicId, [In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] ref object[] parameters, [In, Out] ref bool newValue);
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(12)]
object[,] RefreshData([In, Out] ref int topicCount);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(13)]
void DisconnectData([In] int topicId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(14)]
int Heartbeat();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(15)]
void ServerTerminate();
}
//[ComImport, TypeLibType((short)0x1040), Guid("A43788C1-D91B-11D3-8F39-00C04F3651B8")]
public interface IRTDUpdateEvent
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10), PreserveSig]
void UpdateNotify();
/*
[DispId(11)]
int HeartbeatInterval
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
get;
[param: In]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
set;
}
*/
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(12)]
void Disconnect();
}
}
Ответ
Я бы использовал Publish/Subscribe архитектуру для сообщения между потоками и Inversion of Control для управления жизненным циклом потоков.
Publish/Subscribe это некая шина данных внутри приложения, через которую потоки общаются между собой.
В IoC контейнере в отдельном потоке создаём объект, который работает с COM объектом. Объект через делегат получает сообщение, на его базе создаёт своё внутренне сообщение которое отдаёт в шину данных.
Другие потоки (интерфейс(viewmodel) или другие обработчики) заранее подписаны на сообщения в этой шине данных, получают сообщения и используют по своему усмотрению.
Не сложные к освоению Publish/Subscribe и Ioc.
Nuget:Microsoft.Practices.Prism.PubSubEvents
IoC контейнер NuGet:Unity 4.0.1
Комментариев нет:
Отправить комментарий