C # Preventing call from hanging with CallBack child thread

I have the following code, which in a very specific scenario will hang indefinitely:

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

      

Unfortunately, the hang is out of my control in this very specific scenario.

So I would like to be able to handle this scenario and at least throw an exception.

I would like to spin a child thread that will callback the main thread when it timeouts.

How would you go about it - here is my attempt:

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: Here is my attempt at using Task:

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();
}

      

+3


source to share


5 answers


Why isn't the Timeout property set?

OdbcConnection.ConnectionTimeout = 15

      

Docs on MSDN state:

Unlike the .NET Framework Data Providers for SQL Server and OLE DB, the .NET Framework Data Provider for ODBC does not support setting this property as a connection string value because it is not a valid ODBC keyword. To specify the connection timeout, set the ConnectionTimeout property before calling Open.



Update

I think the error in Mometdb is that there is a read SQL_ATTR_CONNECTION_TIMEOUT

( See source on GitHub ), whereas it should be SQL_ATTR_LOGIN_TIMOUT

, from MSDN :

To specify the connection timeout, set the ConnectionTimeout property before calling Open. This is equivalent to setting the ODBC SQLSetConnectAttr SQL_ATTR_LOGIN_TIMOUT attribute.

I think passing SQL_ATTR_CONNECTION_TIMEOUT=15

to a connection string should work.

+3


source


I'm not sure what you are asking for. But if you are just trying to establish an asynchronous ODBC connection and you would like to handle the error, I think the below will work.



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;
    }
}

      

+2


source


I would do it like this:

  • Make the call function asynchronous;
  • Declare a new task where you are using a connection,
  • Handle the exception in the caller.

    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();
    }
    
          

+1


source


There is a fundamental difference between await / async and Task.Wait:

await (C # reference) - The await expression does not block the thread it is executing on . Instead, it forces the compiler to sign the rest of the asynchronous method as a continuation on the pending task. The control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.

Task.Wait, MSDN - Wait, this is a synchronization method that makes the calling thread wait for the current task to complete. If the current task is not running, the Wait method attempts to remove the task from the scheduler and execute it on the line of the current thread. If it cannot do this, or if the current task is already running, blocks the calling thread until the task completes .

From what I understood, you have a UI app that tries to open the OdbcConnection at some point. This operation takes some time, during which the user interface is blocked.
If I'm right, this is a very small example that doesn't block the 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;
    }
} 

      

+1


source


use this code instead of task:

            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");
            }
        });

      

0


source







All Articles