none
Метод с тайм-аутом. Проблема взаимодействия с глобальными функциями и объектами. RRS feed

  • Вопрос

  • Есть экземпляр класса Core. В процедуре Main консольного приложения, вызываем Core.UpdateData.
    В проекте используется внешняя библиотека, работа с её объектами производится в ф-ии DataRequest.
    Если вызывать DataRequest в оснвном потоке (вставить её в цикл For Each метода UpdateData, то всё работает). Но ф-ия может долго выполняться и мне необходимо реализовать для неё принудительное завершение по тайм-ауту. Написал код. который приведен, ниже. Перестала вызываться ф-ия MyExternalDLL.ReadData(p_KEY), полагаю из-за того что она стала выполняться в отдельном потоке. Насколько, я знаю работать с объектами из основного потока, в дочерних нельзя. :rolleyes: Нужно сделать Invoke. Попытался через Reflection вызвать DataRequest, но всё равно не работает.
    Подскажите поджалуйста как правильно сделать. :confused:

    Imports System.Threading
    Imports System.Reflection
    Module modMain
    Private WithEvents Core as clsCore=new clsCore
    <STAThread()> Sub Main()
        Do While Not Console.KeyAvailable
          Core.UpdateData() 'Обновление данных
          Application.DoEvents()
        Loop
    End Sub
    End Module
    
    Public Class clsCORE
        Private Key As String = ""
        Public Sub UpdateData()
             For Each Key In CNC.Keys
                Method()
             Next
        End Sub
        Private Sub Method()
            Const TimeOut As Int32 = 1000
            Dim [handles] As WaitHandle() = New WaitHandle() {New AutoResetEvent(False)}
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Worker), [handles](0))
            Dim finished As Boolean = WaitHandle.WaitAll([handles], TimeOut, True)
            If finished Then
                Console.WriteLine("Worker function finished sucessfully")
            Else
              Console.WriteLine([String].Format("Worker function time out({0})!", TimeOut))
            End If
        End Sub
        Private Sub Worker(ByVal state As Object)
            Dim are As AutoResetEvent = DirectCast(state, AutoResetEvent)
            Dim MyType As Type = GetType(clsCORE)
            Dim MyMethodInfo As MethodInfo = MyType.GetMethod("DataRequest")
            Dim MyInstance As Object = Activator.CreateInstance(MyType)
            Dim args() As Object = New Object() {Me.Key}
            Dim Value As Object = MyMethodInfo.Invoke(Core, args)
            Console.WriteLine(Value.ToString)
            are.[Set]()
        End sub
        Public Function DataRequest(ByVal p_KEY As String) As Object
            Return MyExternalDLL.ReadData(p_KEY)
        End Function
    End Class
    28 января 2011 г. 9:21

Все ответы

  • Если вы хотите с помощью Invoke вызвать метод DataRequest в основном потоке, то это ни к чему не приведет. Метод Invoke не вызовет метод DataRequest в основном потоке, он просто вызовет DataRequest в том же потоке. Это равносильно просто вызову DataRequest. А если делать графическое приложение и использовать метод Control.Invoke, то да, это приведет к вызову метода в основном потоке, но тогда метод DataRequest вызовется и будет работать до тех пор, пока не отработает, событие таймаута не прервет его. И все идея таймаута потеряет смысл.


    Для связи [mail]
    28 января 2011 г. 15:25
  • Если вы хотите с помощью Invoke вызвать метод DataRequest в основном потоке, то это ни к чему не приведет. Метод Invoke не вызовет метод DataRequest в основном потоке, он просто вызовет DataRequest в том же потоке. Это равносильно просто вызову DataRequest. А если делать графическое приложение и использовать метод Control.Invoke, то да, это приведет к вызову метода в основном потоке, но тогда метод DataRequest вызовется и будет работать до тех пор, пока не отработает, событие таймаута не прервет его. И все идея таймаута потеряет смысл.


    Для связи [mail]


    Подскажите пожалуйста какие классы фреймворка использовать  чтобы решить мою задачу.

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

    Я также пробовал решить эту задачу через System.Threading.Tasks (TPL + Cancellation) и в этом случае Return MyExternalDLL.ReadData(p_KEY) тоже возвращает 0.

    Мне необходимо написать сервер, который рассылает киентам параметры полученные с оборудования по сети:

    1. Клиент-сервер я написал
    2. В сервере необходимо подключиться к удалённому оборудованию (это занимает время).
    3. В сервере в цикле обновляю  параметры оборудования, связь ненадёжная поэтому, чтобы сервер не подвисал нужно ввести отмену считывания параметра по тайм-ауту.
    4. По завершению цикла клиентам разрешается получение данных.

    Как реализовать 3. другим способом ?

    28 января 2011 г. 18:35
  • Если в методе есть цикл, то завершить его можно при прохождении очередной итерации цикла, примерно так.

     

     class MainClass
      {
        
        static void Main()
        {
          Runner r = new Runner(10000);
          Action act = new Action(r.Run);
          act.BeginInvoke(null, null);
          Console.ReadLine();
        }
      }
    
      public class Runner
      {
        DateTime _start;
        Int32 _timeOut;
        private Runner() { }
        public Runner(Int32 timeOut)
        {
          if (timeOut <= 0)
            throw new ArgumentOutOfRangeException("timeOut");
          _timeOut = timeOut;
        }
        public void Run()
        {
          _start = DateTime.Now;
          while (true)
          {
            if ((DateTime.Now - _start).TotalMilliseconds > _timeOut)
              break;
            //работа
            System.Threading.Thread.Sleep(100);
          }
        }
      }
    

     

    7 апреля 2011 г. 14:10