You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
csharp-basics/14. Exceptions, Threads and...

8.2 KiB

Exceptions,

Threads and Tasks


Exceptions

  • Exceptions are problems that occur during the lifetime of the program
    • Typical exceptions are IndexOutOfRangeException and NullReferenceException
    • IndexOutOfRangeException occurs when trying to access an element of an object using an index greater than the number of elements in that object
    • NullReferenceException occurs when trying to reference a variable that has not been assigned a value

Exceptions (continued)

  • There should be some kind of system to check for these problems so that even if they do occur, the application does not crash
    • For example, if a user tries to read from a file that does not exist, the user should be given an informative error message instead of closing the application

try - catch

Wrap the code that could result in an exception in a __try __ block

Wrap the code that should execute if an exception occurs in a __catch __ block

string path = @"C:\Users\Public\TestFolder\WriteText.txt";

try

{

string text = System.IO.File.ReadAllText(path);

}

catch(DirectoryNotFoundException e)

{

Console.WriteLine($"The path {path} does not exist.");

}

throw

__throw __ - keyword results in the exception that is caught in the catch block

This can be used if you want to execute some code before stopping the execution of the program

string path = @"C:\Users\Public\TestFolder\WriteText.txt";

try

{

string text = System.IO.File.ReadAllText(path);

}

catch (IOException e)

{

Console.WriteLine("An exception occurred.");

Console.WriteLine("Logging exception information...");

Console.WriteLine($"Exception: {e.Message}");

throw;

}

finally

__finally __ block executes after try-catch regardless of whether an exception occurred or not

This is useful for closing unmanaged objects like database connections and file streams

try

{

// Open a database connection or file stream

}

catch (SystemException e)

{

// Do this if connection / stream fails

}

finally

{

// Release the unmanaged resources from memory

}

Exercise 1: Debugging Exceptions

Create a savings calculator which asks the user for a target amount of money the user is aiming for (in integers), and the users monthly salary. For parsing, use the int.Parse() method instead of int.TryParse().

The program then calculates the number of months it will take the user to reach the goal.

After displaying the result, the user is then asked for a path to a _folder _ where the result is then saved as months_until_target.txt.

Try your program with different user inputs. Try to intentionally break your program and find as many exceptions as you can.

Handle the exceptions with exception handling, and inform the user of each exception with an appropriate message.

Threads

  • You might have time consuming functionality in your program, like
    • heavy algorithms and mathematical calculations, or
    • reading a large amount of data from a file
  • You could also have some continuous stream of data being read, like
    • serial signal
  • In case of time consuming methods, it might be a bad idea to wait for the execution to finish before continuing the main program
    • This would show up to the user as the program being stuck
    • At the very least, there could be some progress bar to indicate that the program hasn't crashed
  • In the case of continuous execution, there needs to be some way to read the stream and execute the main program at the same time

Threads (continued)

.NET supports __multithreading __ and classes to add new threads are included in the base class library

using System.Threading;

Separate tasks can be executed simultaneously on separate threads

Separate threads run on different cores in the processor

All threads have access to the resources allocated to your program

Initialize a ThreadStart object with the method to be threaded as the argument:

public static void PrintInteger()

...

ThreadStart printIntegerStart = new ThreadStart(PrintInteger);

Initialize a Thread object with the ThreadStart object set as the argument, and start the new thread with Thread.Start() method:

Thread printIntegerThread = new Thread(printIntegerStart);

printIntegerThread.Start();

Threads - Example

class Program

{

public static void PrintInteger()

{

Console.WriteLine("Thread started.");

int val = 0;

while (true)

{

Thread.Sleep(100);

Console.WriteLine("Current value: " + val);

++val;

}

}

static void Main(string[] args)

{

ThreadStart printIntegerStart = new ThreadStart(PrintInteger);

Console.WriteLine("Starting thread...");

Thread printIntegerThread = new Thread(printIntegerStart);

printIntegerThread.Start();

Console.WriteLine("Main thread continuing.");

}

}

The PrintInteger method prints a value, waits for 0.1sec, increases the value, prints it again and so on

The method keeps executing indefinitely, but the main thread continues execution as well

Threads (continued)

You can create as many threads as you like:

ThreadStart printIntegerStart = new ThreadStart(PrintInteger);

Console.WriteLine("Starting thread...");

Thread printIntegerThread = new Thread(printIntegerStart);

printIntegerThread.Start();

Thread printIntegerThread2 = new Thread(printIntegerStart);

printIntegerThread2.Start();

Thread printIntegerThread3 = new Thread(printIntegerStart);

printIntegerThread3.Start();

Exercise 2: Threaded Loading

Create a method public static void LoadData() which simulates loading resources by printing progress from 0% to 100%. Increase the progress in a loop with 1% increments. Use the Thread.Sleep() method to wait 10 ms between each increment.

Create a main loop where each time the user presses enter, the LoadData() method is executed on a separate thread.


Näytä ekstrana, miten tämä tehdään käyttäen luokan metodia eri luokan arvoilla

Tasks

  • In some cases you want to continue working on some tasks while others are queued to be executed
    • For example, after sending a server request, you might want to begin some tasks immediately and others only after a response has been received
  • Following this great analogy from Microsoft docs, a cook doesn't have to wait for the eggs to be ready to begin brewing coffee or cooking bacon; instead, the cook would begin all tasks simultaneously
  • Threading won't help here; even if there was one cook (thread) for each task, they would all have to wait for the oven to heat up. One cook can just well do all the tasks (cooking) alone.

Tasks - Continued

To create an asynchronous method, assign it with the async keyword. It is good practice to end the method name with Async

The method should return a value of the type that is specified in the angled brackets (if your task doesn't return a value, leave the brackets out)

Inside the method, call an asynchronous method with the await keyword:

static async Task MyMethodAsync()

{

byte[] content = await client.GetByteArrayAsync("https://docs.microsoft.com/en-us/");

return content.Length;

}

This method can now be called asynchronously:

Task myMethodResult = MyMethodAsync();

Tasks - Example

class Program

{

static async Task GetResultAsync()

{

int waitTime = 5000;

await Task.Delay(waitTime); // Replace this with your time consuming async functionality

return new string($"The task took {waitTime / 1000} seconds to finish.");

}

static void Main(string[] args)

{

Console.WriteLine("Starting operation...");

Task resultTask = GetResultAsync();

Console.WriteLine("Operation has been started.");

Console.WriteLine("In the meantime, tell me something nice: ");

string somethingNice = Console.ReadLine();

resultTask.Wait(); // Wait for the task to complete

Console.WriteLine(resultTask.Result);

Console.ReadKey();

}

}

Exercise 3: Asynchronous Loading

Re-create the exercise 2, but instead of using separate threads for "loading the data", use an asynchronous method LoadDataAsync()