Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Greenhorn
Member

Откуда:
Сообщений: 311
Добрый день.

Задача:
В главном потоке необходимо асинхронно выполнять поток команд к SQL_ю.
Результат выполнения в главном потоке не нужен.
Вся обработка результатов команд (успешных и нет) находится в CallBack_е.
Поэтому очень важно контролировать максимальное время выполнения каждой команды.

Проблема : "Свойство CommandTimeout игнорируется во время вызовов асинхронного метода, такого как BeginExecuteReader."

+ жопное решение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlTypes;

namespace Test_Async
{
  class Program
  {
    static void ProcessAsyncMS(IAsyncResult result)
    {
      SqlCommand cmd = null;
      try
      {
        cmd = (SqlCommand)result.AsyncState;
        SqlDataReader reader = cmd.EndExecuteReader(result);

        if (reader != null)
        {
          while (reader.Read())
            Console.WriteLine("Read RS");
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine("Err:" + ex.Message);
      }
      finally 
      {
        if (cmd != null)
        {
          SqlConnection con = cmd.Connection;
          if (con != null)
            con.Close();
        }
      }
    }

    private static void TimerCallback(Object obj)
    {
      IAsyncResult result = (IAsyncResult)obj;

      if (!result.IsCompleted)
      { 
        SqlCommand cmd = (SqlCommand)result.AsyncState;
        cmd.Cancel();
      }
    }

    static void Main(string[] args)
    {
      SqlConnection con = new SqlConnection("Persist Security Info=False;Integrated Security=true;server=(local)");
      
      con.Open();

      SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:10:03'; SELECT 1/0 as [QQQ]", con);
      cmd.CommandTimeout = 1;

      AsyncCallback CB = new AsyncCallback(ProcessAsyncMS);

      IAsyncResult result = cmd.BeginExecuteReader(CB, cmd);
      Timer t = new Timer(
            TimerCallback
          , result
          , 1000
          , Timeout.Infinite
        );

      Console.ReadLine();
    }
  }
}


А как правильно решить такую задачу ?
23 июл 15, 12:27    [17925180]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Antonariy
Member

Откуда: ☭
Сообщений: 72988
Greenhorn
А как правильно решить такую задачу ?
задать таймаут на сервере?
23 июл 15, 13:24    [17925434]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
Greenhorn
А как правильно решить такую задачу ?

Сделать синхронное выполнение запроса в асинхронном таске c заданным таймаутом.
Antonariy
задать таймаут на сервере?

У запросов (не транзакций) этот таймаут server-scoped.
23 июл 15, 14:16    [17925651]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Greenhorn
Member

Откуда:
Сообщений: 311
Сон Веры Павловны
Greenhorn
А как правильно решить такую задачу ?

Сделать синхронное выполнение запроса в асинхронном таске c заданным таймаутом.
Antonariy
задать таймаут на сервере?

У запросов (не транзакций) этот таймаут server-scoped.


В асинхронном таске, конечно, можно.
Одна беда - при синхронном выполнении запроса (в другом потоке) точно потеряем поток.
А в случае асинхронного выполнения есть шанс, что поток останется в пуле потоков и будет востребован только тогда, когда запрос реально выполнится.
Именно по этому я и выбрал CallBack_и ...

Antonariy, какой таймаут на сервере ? "Remote Query TimeOut" ?
Ну во первых - каждый запрос имеет разное значение MaxCommandTimeOut (от 5 сек. до 5 мин.)
Во вторых это повлияет на всех, а не только на мою "маленькую" задачку.

Еще идеи ?
23 июл 15, 14:50    [17925766]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
Greenhorn
Одна беда - при синхронном выполнении запроса (в другом потоке) точно потеряем поток.
А в случае асинхронного выполнения есть шанс, что поток останется в пуле потоков и будет востребован только тогда, когда запрос реально выполнится.

С какого перепугу? Task'и из TPL работают именно с пулом потоков:
For example, beginning with the .NET Framework 4 you can create Task and Task<TResult> objects, which perform asynchronous tasks on thread pool threads.

https://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.110).aspx
23 июл 15, 14:58    [17925820]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Greenhorn
Member

Откуда:
Сообщений: 311
Сон Веры Павловны
Greenhorn
Одна беда - при синхронном выполнении запроса (в другом потоке) точно потеряем поток.
А в случае асинхронного выполнения есть шанс, что поток останется в пуле потоков и будет востребован только тогда, когда запрос реально выполнится.

С какого перепугу? Task'и из TPL работают именно с пулом потоков:
For example, beginning with the .NET Framework 4 you can create Task and Task<TResult> objects, which perform asynchronous tasks on thread pool threads.

https://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.110).aspx

С такого перепуга, что SqlCommand.ExecuteReader() в отдельном таске и в отдельном потоке из пула потоков будет использовать этот поток, пока не отработает -> Я так думаю !
Если Вы имеете информацию об обратном - огромная просьба - поделитесь ...
23 июл 15, 15:23    [17925987]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Greenhorn
Member

Откуда:
Сообщений: 311
Сон Веры Павловны,

А вот и подтверждение того, что я прав:
+ Новый пример
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlTypes;

namespace Test_Async
{
  class Program
  {
    private static bool isCompete;
    static void ProcessAsyncMS(IAsyncResult result)
    {
      SqlCommand cmd = null;
      try
      {
        cmd = (SqlCommand)result.AsyncState;
        SqlDataReader reader = cmd.EndExecuteReader(result);

        if (reader != null)
        {
          while (reader.Read())
            Console.WriteLine("Read RS");
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine("Err:" + ex.Message);
      }
      finally 
      {
        if (cmd != null)
        {
          SqlConnection con = cmd.Connection;
          if (con != null)
            con.Close();
        }
      }
      isCompete = true;
    }

    private static void TimerCallback(Object obj)
    {
      IAsyncResult result = (IAsyncResult)obj;

      if (!result.IsCompleted)
      { 
        SqlCommand cmd = (SqlCommand)result.AsyncState;
        cmd.Cancel();
      }
    }

    static void DoTestCallBack()
    {
      SqlConnection con = new SqlConnection("Persist Security Info=False;Integrated Security=true;server=(local)");

      con.Open();

      SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:10:03'; SELECT 1/0 as [QQQ]", con);
      cmd.CommandTimeout = 1;

      AsyncCallback CB = new AsyncCallback(ProcessAsyncMS);

      IAsyncResult result = cmd.BeginExecuteReader(CB, cmd);
      Timer t = new Timer(
            TimerCallback
          , result
          , 1000
          , Timeout.Infinite
        );
    }

    static void DoTestTask()
    {
      Action<object> action = (object obj) =>
      {
        SqlConnection con = new SqlConnection("Persist Security Info=False;Integrated Security=true;server=(local)");

        con.Open();

        SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:03'; SELECT 1/0 as [QQQ]", con);
        cmd.CommandTimeout = 1;
        try
        {
          SqlDataReader reader = cmd.ExecuteReader();
          while (reader.Read())
            Console.WriteLine("Read RS");
        }
        catch (Exception ex)
        {
          Console.WriteLine("Err:" + ex.Message);
        }
        finally
        {
          con.Close();
        }
      };

      Task t = new Task(action, "alpha");
      t.Start();

      while (!t.Wait(500))
      {
        int workerThreads;
        int completionPortThreads;
        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

        Console.WriteLine(String.Format("workerThreads: {0} ; completionPortThreads: {1}", workerThreads, completionPortThreads));
      }
    }

    static void Main(string[] args)
    {
      DoTestCallBack();

      while (!isCompete)
      {
        int workerThreads;
        int completionPortThreads;
        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

        Console.WriteLine(String.Format("workerThreads: {0} ; completionPortThreads: {1}", workerThreads, completionPortThreads));
        Thread.Sleep(500);
      }

      Console.WriteLine("END DoTestCallBack()");

      DoTestTask();
      
      Console.WriteLine("ALL DONE");
      Console.ReadLine();
    }
  }
}


+ Выхлоп
workerThreads: 1023 ; completionPortThreads: 1000
workerThreads: 1023 ; completionPortThreads: 1000
workerThreads: 1023 ; completionPortThreads: 1000
Err:При выполнении текущей команды возникла серьезная ошибка.. При наличии результатов они должны быть аннулированы.
Операция отменена пользователем.
END DoTestCallBack()
workerThreads: 1022 ; completionPortThreads: 1000
workerThreads: 1022 ; completionPortThreads: 1000
Err:Истекло время ожидания (Timeout). Время ожидания истекло до завершения операции или сервер не отвечает.
ALL DONE


Хотя при таком кол-ве потоков, может я на воду дую ?
23 июл 15, 16:28    [17926458]     Ответить | Цитировать Сообщить модератору
 Re: BeginExecuteReader и CommandTimeout. Как правильно ?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
Greenhorn
А вот и подтверждение того, что я прав

А с чего вы взяли, что внутри
      while (!t.Wait(500))
      {
        int workerThreads;
        int completionPortThreads;
        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

        Console.WriteLine(String.Format("workerThreads: {0} ; completionPortThreads: {1}", workerThreads, completionPortThreads));
      }

по истечении таймаута таск уже должен отдать поток? таск еще жив, и держит этот поток. Вы вставьте вывод результата ThreadPool.GetAvailableThreads после того же ReadLine в конце теста, и посмотрите результат.
23 июл 15, 18:41    [17927135]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить