C # Prevent połączeń Hang z CallBack Dziecko wątku

głosy
3

Mam następujący kod, który w bardzo konkretnym scenariuszu zawiśnie na czas nieokreślony:

connection = new OdbcConnection(connectionString);
connection.Open();

Niestety, hang jest poza moją kontrolą, w tym bardzo konkretnym scenariuszu.

Więc chciałbym być w stanie obsługiwać ten scenariusz i co najmniej wyjątek.

Chciałbym się kręcić z gwintem dzieci, które będą oddzwonienia główny wątek, gdy limity czasu.

Jak to zrobić - tu jest mój próba:

OdbcConnection connection = null;

var timeout = TimeSpan.FromSeconds(15);
var resetEvent = new ManualResetEvent(false);
bool exceptionThrown = false;

var connectionThread = new Thread(() =>
{
    try
    {
        connection = new OdbcConnection(connectionString);
        connection.Open();
    }
    catch(Exception e)
    {
        exceptionThrown = true;
    }
    finally
    {
        resetEvent.Set();
    }
});

connectionThread.Start();

var isOk = resetEvent.WaitOne(timeout);

if(exceptionThrown)
{
   throw now Exception(Exception connection to DB);
}

if (!isOk)
{
    connectionThread.Abort();
    const string messageFormat = Timeout of {0} reached while creating OdbcConnection to {1}.;
    throw now Exception(string.Format(messageFormat, timeout, connectionString));
}

UPDATE: Oto moja próba korzystania zadanie:

OdbcConnection connection = null;
var connectionTask = Task.Factory.StartNew(() =>
{
    connection = new OdbcConnection(connectionString);
    connection.Open();
    Thread.Sleep(3000);
});
try
{
    connectionTask.Wait(1000);       // Wait for 1 second.
}
catch (AggregateException ex)
{
    Console.WriteLine(Exception in connection);
}

bool completed = connectionTask.IsCompleted;
if(!completed)
{
    Console.WriteLine(Connection Timed-out);
}
else
{
    connection.DoSomething();
}
Utwórz 06/04/2017 o 00:51
źródło użytkownik
W innych językach...                            


5 odpowiedzi

głosy
3

Dlaczego nie ustawienie właściwości Timeout?

OdbcConnection.ConnectionTimeout = 15

Docs na MSDN stanu:

W przeciwieństwie do .NET Framework dostawców danych dla SQL Server i OLE DB Provider Framework danych ODBC nie obsługuje ustawienie tej właściwości jako wartość ciąg połączenia, ponieważ nie jest to prawidłowy kluczowe połączenie ODBC. Aby określić limit czasu połączenia, należy ustawić właściwość ConnectionTimeout przed wywołaniem Otwórz.

Aktualizacja

Myślę, że błąd w Mometdb jest to, że nie czyta SQL_ATTR_CONNECTION_TIMEOUT( patrz źródła na GitHub ), podczas gdy powinno być SQL_ATTR_LOGIN_TIMOUT, z MSDN :

Aby określić limit czasu połączenia, należy ustawić właściwość ConnectionTimeout przed wywołaniem Otwórz. Jest to równoważne ustawieniu atrybut ODBC SQLSetConnectAttr SQL_ATTR_LOGIN_TIMOUT.

Myślę, przechodząc SQL_ATTR_CONNECTION_TIMEOUT=15do ConnectionString powinny działać.

Odpowiedział 12/04/2017 o 19:19
źródło użytkownik

głosy
2

Nie jestem pewien, co jest dokładne wymagania. Ale jeśli jesteś po prostu próbuje nawiązać połączenie ODBC asynchronicznie i chcesz zrobić obsługę błędów to myślę poniżej powinno działać.

class Program
{
    static void Main(string[] args)
    {
        string connstring = "connstring");
        try
        {
            Program.Method(connstring);
        }
        catch(Exception ex)
        {
            var m = ex.Message;
        }
    }

    static async Task<int> Method(string connstring)
    {
        try
        {
            OdbcConnection conn = new OdbcConnection(connstring);

            await conn.OpenAsync();
        }
        catch (Exception ex)
        {
            var m = ex.Message;
        }
        return 1;
    }
}
Odpowiedział 13/04/2017 o 02:57
źródło użytkownik

głosy
1

Istnieje zasadnicza różnica między Oczekujcie / asynchroniczny i Task.Wait:

oczekiwać (C # odniesienia) - Wyrażenie Await nie blokuje gwintu, w którym jest wykonywany . Zamiast tego, to powoduje, że kompilator, aby się zarejestrować resztę metody asynchronicznego jako kontynuacja na oczekiwane zadania. Kontrola po czym wraca do wywołującego metody asynchronicznej. Po zakończeniu zadania, wywołuje jego kontynuację, a wykonanie metody asynchronicznej wznawia którym zostało przerwane.

Task.Wait, MSDN - Wait to metoda synchronizacji powoduje, że wątek wywołujący czekać aż obecny zadanie zostało zakończone. Jeśli bieżący zadanie nie rozpoczął realizacji, sposób Wait próbuje usunąć zadanie z terminarza i wykonać go inline od aktualnego wątku. Jeśli nie jest w stanie tego zrobić, lub jeśli obecny zadanie rozpoczęła już realizację, blokuje wątek wywołujący aż zakończeniu zadania .

Z tego, co zrozumiałem, masz aplikacji UI, które w pewnym momencie próby otwarcia OdbcConnection. Ta operacja wymaga trochę czasu, podczas którego UI jest zablokowany.
Jeśli się nie mylę, jest to bardzo mały przykład, który nie blokuje UI:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        bool completed = await Task.Run(() => DoSomethingLong());

        if (!completed)
        {
            Console.WriteLine("Not completed");
        }
        else
        {
            Console.WriteLine("Completed");
        }
    }

    bool DoSomethingLong()
    {
        //This loop will take 15 seconds
        for (int i = 0; i < 30; i++)
        {
            if (i % 10 == 0)
                Console.WriteLine("<DoSomethingLong> - I am alive");

            Thread.Sleep(500);
        }

        return true;
    }
} 
Odpowiedział 13/04/2017 o 13:53
źródło użytkownik

głosy
1

Chciałbym zrobić to w ten sposób:

  • Dokonać asynchronicznie funkcji rozmówcy;
  • Deklarować nowe zadania, w którym robisz rzeczy z przyłączeniem;
  • Uchwyt wyjątek wywołującego.

    static async void Main(string[] args)
    {
        try
        {
            await DoSomething("");
        }
        catch (TimeoutException ex)
        {
            // Handle Exception.
        }
        catch (SomeOtherException ex)
        {
            // Handle Exception.
        }
    }
    
    static Task DoSomething(string connectionString)
    {
        OdbcConnection connection = new OdbcConnection(connectionString);
        connection.Open();
        connection.DoSomethingElse();
    }
    
Odpowiedział 12/04/2017 o 19:23
źródło użytkownik

głosy
0

użyć tego kodu, zamiast zadania:

            OdbcConnection connection = null;
        var connectionTask = Task.Factory.StartNew(() =>
        {
            connection = new OdbcConnection(connectionString);
            connection.Open();
            Thread.Sleep(3000);
        });

        connectionTask.ContinueWith((tsk) => 
        {
            if(tsk.Exception==null && tsk.IsCompleted)
            {
                connection.DoSomething();
            }
            else
            {
                // here you can examine exception that thrown on task
                Console.WriteLine("Connection Timed-out");
            }
        });
Odpowiedział 16/04/2017 o 09:09
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more