8
\$\begingroup\$

Currently, my team and I have 2 types of databases that are updated periodically, SQL Server and Access. Also, we run specialized reports based on those databases.

Basically, we have 4 tasks that need to be completed:

  1. Update SQL Server
  2. Publish SQL Server Reports
  3. Update Access Databases
  4. Publish Access Db reports

Access takes forever to run, so I am trying to make my code run the task of Updating Access and Publishing Access reports at the same time that I am updating SQL Server and publishing those reports.

Can someone verify if I am on the right path, or maybe share a link that better explains async?

static void Main(string[] args)
{
    var cs1 = new _1_UpdateSQL();
    var cs2 = new _2_PublishSQLRpts();

    Task t = TriggerAccess();

    cs1.UpdateSqlCodeOnly();
    cs2.PublishSqlRptsCodeOnly();
    t.Wait();
}


static async Task TriggerAccess()
{
    var cs3 = new _3_UpdateAccessDb();
    var cs4 = new _4_PublishAccessRpts();


    await Task.Run(() => cs3.UpdateAccessDbCodeOnly());
    await Task.Run(() => cs4.PublishAccessRptsCodeOnly());

}
\$\endgroup\$
  • \$\begingroup\$ Have you actually tried this code? This can't work, trying to Start() and async Task will throw an exception. \$\endgroup\$ – svick Aug 5 '14 at 20:42
  • \$\begingroup\$ You are correct. I have edited the code and removed Start(). I got an error when I tested the code. I removed it, and now it runs successfully! \$\endgroup\$ – suexicano Aug 6 '14 at 16:40
  • \$\begingroup\$ are you asking us to tell you the next step in your development of this code? \$\endgroup\$ – Malachi Aug 6 '14 at 17:26
  • \$\begingroup\$ @Malachi - No, I just wanted to make sure that I was using asyn correctly based on my research. \$\endgroup\$ – suexicano Aug 7 '14 at 19:13
9
\$\begingroup\$

When I have to manage multiple threads, I make sure all processing is complete, typically by using WaitAll.

List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() => { TriggerAccess(); }));
tasks.Add(Task.Run(() => { TriggerSQL(); }));
Task.WaitAll(tasks.ToArray());

This also allows me to use methods in a non-threaded capacity for testing and to easily add additional threads when the need arises. I can then also return actual data from a method, if I need to, instead of just a thread.

The difference between running the method in a thread and having the method be an async thread is that an async method waits until the first await statement to return to the main thread for processing whereas threading the method immediately continues to the next statement in the main method.

http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx and http://msdn.microsoft.com/en-us/library/hh156513.aspx are very helpful for me.

\$\endgroup\$
1
\$\begingroup\$

What you're doing is certainly one way to do it, but I have some notes about the code:

You don't really need async here. async is useful in UI applications, because it allows you to easily offload code from the UI thread and it's useful in server applications, because it uses less threads so it makes them more scalable.

But your application doesn't fall under either case, so there is no reason for your to use async, and you can just replace each await task with task.Wait() (or task.Result, if you used the result of the await).


await Task.Run(() => cs3.UpdateAccessDbCodeOnly());
await Task.Run(() => cs4.PublishAccessRptsCodeOnly());

I don't see why do you have two Tasks here, you could combine them into one:

await Task.Run(() =>
{
    cs3.UpdateAccessDbCodeOnly();
    cs4.PublishAccessRptsCodeOnly()
});

(Formatted like this, it looks like more code, but it's actually less.)


You have a Task for Access, but you could also have another Task for SQL. This will make your code more symmetric and clearer, but also longer and also slightly less efficient.


With the changes mentioned above, your code could look like this:

static void Main()
{
    Task.WaitAll(new[] { Task.Run(TriggerSql), Task.Run(TriggerAccess) });
}

static void TriggerSql()
{
    var cs1 = new _1_UpdateSQL();
    var cs2 = new _2_PublishSQLRpts();

    cs1.UpdateSqlCodeOnly();
    cs2.PublishSqlRptsCodeOnly();
}

static void TriggerAccess()
{
    var cs3 = new _3_UpdateAccessDb();
    var cs4 = new _4_PublishAccessRpts();

    cs3.UpdateAccessDbCodeOnly();
    cs4.PublishAccessRptsCodeOnly();
}

Those are some really weird class names. I understand that you can't start the class name with a number, which is why you're using the first underscore. But why would you want to start the class name with a number? Name your types based on what they do, not based on what order you use them in, or something like that.

Also, I find it weird that the only public members of your classes seem to be the default constructor and a single method. Have you considered another design, like static methods?

\$\endgroup\$
0
\$\begingroup\$

You haven't indicated that these processes are somehow related. I'm guessing that updating SQL Server and Access can be done separately. The only commonality I see here is that they are both run at a set interval.

My advice would be to avoid async and threading entirely. Split this functionality so they run in their own processes and avoid having to deal with any async/threading complexity and Heisenbugs.

Your code will also be much much shorter.

Process 1

static void Main(string[] args)
{
    var cs1 = new _1_UpdateSQL();
    var cs2 = new _2_PublishSQLRpts();

    cs1.UpdateSqlCodeOnly();
    cs2.PublishSqlRptsCodeOnly();
}

Process 2

static void Main(string[] args)
{
    var cs3 = new _3_UpdateAccessDb();
    var cs4 = new _4_PublishAccessRpts()

    cs3.UpdateAccessDbCodeOnly()
    cs4.PublishAccessRptsCodeOnly()  
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.