Monthly Archives: March 2020

Multithreading

Multithreading becomes absolutely necessary as soon as you start working with GUI. You cannot let the system freeze and your users asking themselves if the program crashed each time you let a long operation run, that is why you should call async methods. An async method has to declare await in order to free the main thread, otherwise it will run synchronously.

Starting an asynchronous task

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Main");

            AsyncMethod("AsyncTask1");
            AsyncMethod("AsyncTask2");

            Console.WriteLine("Main Thread continues doing something else");
            Thread.Sleep(2000);

            Console.WriteLine("Leaving Main");
        }

        private static async Task AsyncMethod(string message)
        {
            await Task.Run(() => LongSyncRunningMethod(message));
        }

        private static void LongSyncRunningMethod(string message)
        {
            Console.WriteLine($"Starting {message}");
            Thread.Sleep(1000);
            Console.WriteLine($"{message} done");
        }
    }
}

One might instinctively think, that LongSyncRunningMethod and its Console.WriteLine($"Starting {message}"); would be called before Console.WriteLine("Main Thread continues doing something else"); but it actually depends on the task scheduler. In some cases it will be true, in other cases not.

Starting Main
Starting AsyncTask1
Main Thread continues doing something else
Starting AsyncTask2
AsyncTask1 done
AsyncTask2 done
Leaving Main

Starting an asynchronous task and getting a return value

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Main");

            AsyncCalculation();

            Console.WriteLine("Main Thread continues doing something else");
            Thread.Sleep(2000);

            Console.WriteLine("Leaving Main");
        }

        private static async Task AsyncCalculation()
        {
            int result = await Task.Run(() => LongCalculation());
            Console.WriteLine($"Calculated value: {result}");
        }

        private static int LongCalculation()
        {
            Thread.Sleep(1000);
            return 101;
        }
    }
}
Starting Main
Starting AsyncCalculation
Main Thread continues doing something else
Calculated value: 101
Leaving Main

Catching Exception in an asynchronous task

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Main");

            AsyncExceptionMethod();

            Console.WriteLine("Main Thread continues doing something else");
            Thread.Sleep(2000);

            Console.WriteLine("Leaving Main");
        }

        private static async Task AsyncExceptionMethod()
        {
            await Task.Run(() => ThrowException())
                .ContinueWith(task => HandleTaskException(task));
        }

        private static void ThrowException()
        {
            throw new Exception();
        }

        private static void HandleTaskException(Task task)
        {
            if (task.Exception != null)
            {
                Console.WriteLine("Exception caught");
            }
        }
    }
}
Starting Main
Starting AsyncExceptionMethod
Main Thread continues doing something else
Exception caught
Leaving Main

As the task run in another thread, a thrown exception will not be caught in the main thread. The method ContinueWith allows the user to run a check after the task is completed and handle accordingly. In this case ContinueWith accepts a lambda expression with Task as parameter, we can check the status of the completed task, for example if it was completed, cancelled or faulted.

await Task.Run(() => LongCalculation)
    .ContinueWith(task =>
{
    if (task.IsCanceled)
        DoSomethingWhenCancelled();
    else if (task.IsFaulted)
        DoSomethingOnError(task .Exception);
    else 
        // task.IsCompleted
        DoSomethingWhenComplete();
});

https://blog.stephencleary.com/2012/02/async-and-await.html

https://johnthiriet.com/removing-async-void/

Format string

I usually try to comment and log a lot when I write a program, I never regret it. But in order to write information that is actually meaningful you need to get the runtime context, like the current value of some variables or what exception has been thrown. Furthermore you will want to facilitate comprehension for the reader, if you work with hexadecimal values, you will also want to show your value as hexadecimal and spare the reader the conversion. Here are some of the most useful functions for formatting your strings and allowing you to easily write messages that matter and that are comprehensive.

Formatting strings

$ – string interpolation

String Interpolation is available since C# 6, with it you can compose a string with other variables and make your code actually fairly readable for people.

int value = 255;
Console.WriteLine($"The hexadecimal value of {value} is 0x{value.ToString("X")}");
The hexadecimal value of 255 is 0xFF

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated

@ – verbatim string literal

string filename1 = @"C:\Windows\System32\shell32.dll";
string filename2 = "C:\\Windows\\System32\\shell32.dll";
Console.WriteLine(filename1);
Console.WriteLine(filename2);
C:\Windows\System32\shell32.dll
C:\Windows\System32\shell32.dll

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim

Formatting numbers

The ToString() method accepts a string as Parameter, it consist of a letter, big or small and optionally an associated number, which sets how many letters the resulting string should at least have.

X – hexadecimal

int intValue = 255;
// hexadecimal format
Console.WriteLine(intValue.ToString("x"));
Console.WriteLine(intValue.ToString("X4"));
ff
00FF

https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

F – fixed-point

int intValue = 255;
double doubleValue = 1234.5678;
// fixed-point format
Console.WriteLine(intValue.ToString("F"));
Console.WriteLine(doubleValue.ToString("F"));
Console.WriteLine(doubleValue.ToString("F3"));
255.00
1234.56
1234.567

0 – zero placeholder

double doubleValue = 1234.5678;
// 0 placeholder
Console.WriteLine(doubleValue.ToString("000"));
Console.WriteLine(doubleValue.ToString("00000.00"));
Console.WriteLine(doubleValue.ToString("00000.00000"));
1235
01234,57
01234,56780

https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings

# – digit placeholder

double doubleValue = 1234.5678;
// digit placeholder
Console.WriteLine(doubleValue.ToString("###"));
Console.WriteLine(doubleValue.ToString("#####.##"));
Console.WriteLine(doubleValue.ToString("#####.#####"));
1235
1234,57
1234,5678

Convert – string to int

// base 10 string
string intValue = "64";
Console.WriteLine(Convert.ToInt32(intValue));

// base 16 string
string hexValueAsString = "0x64";
Console.WriteLine(Convert.ToInt32(hexValueAsString, 16));
64
100

https://docs.microsoft.com/de-de/dotnet/api/system.convert.tostring?view=netcore-3.1#System_Convert_ToString_System_Int16_System_Int32_

BitConverter – int to byte array

int value = 511;
byte[] bytes = BitConverter.GetBytes(value);
Console.WriteLine(BitConverter.ToString(byteArray));
127-01-00-00

https://docs.microsoft.com/de-de/dotnet/api/system.bitconverter.getbytes?view=netcore-3.1#System_BitConverter_GetBytes_System_Int32_

string.Join() – Array to string

int[] values = new int[] { 1, 2, 3, 4, 255 };
Console.WriteLine(string.Join(",", values));
Console.WriteLine(string.Join(",", values.Select(b => b.ToString("X2"))));
1,2,3,4,255
01,02,03,04,FF

https://docs.microsoft.com/de-de/dotnet/api/system.string.join?view=netcore-3.1#System_String_Join_System_String_System_Collections_Generic_IEnumerable_System_String__