0

I have programmed a bulb production simulation in C#. When a user enters option 1 in the command line interface an asynchronously function starts simulating the production of 10,000 bulbs whereby 5% of bulbs are defect. If a defect bulb is detected, an observer will be notified and display a message in the command prompt. I have noticed that the keyword await if it is placed directly before prod.startMaschine(maschineID); causes the program to not work anymore. So I have removed it. I wonder now when to use await and when not to. Can you explain it with my example program?

Console.WriteLine("Bulb Production Monitoring!");
Main();
static async Task Main() {
    string input = "";
    int maschineID = 1;
    Production prod = new Production();
    Console.WriteLine("1: starts a production maschine");
    Console.WriteLine("2: exits the production process");
    while(!input.Equals("exit")) {
        input=Console.ReadLine();
        switch(input) {
            case "1": 
                prod.registerObserver(new Observer());
                prod.startMaschine(maschineID); 
                maschineID++; 
                break;
            case "2" : 
                input="exit"; break;
            default: 
                break;
        }
    }
}
    
public class Bulb {
    public bool _functionTest;
}
    
public class Production {
    private List<Observer> _observerList = new List<Observer>();
    public async Task startMaschine(int maschineId) {
        Console.WriteLine($"Maschine {maschineId} is ready for use ... ");
        int i=0;
        Random rndGenerator = new Random();
        await Task.Run(
            async ()=> {
                while(i<10000) {
                    int rndNumber=rndGenerator.Next(1,101);
                    Bulb bulb = new Bulb();
                    await Task.Delay(2000);
                    if (rndNumber<=5) {
                        bulb._functionTest=false;
                        this.notify(maschineId);
                    }
                    else {
                        bulb._functionTest=true;
                    }
                }
            }
        );
    }
    
    public void registerObserver(Observer obs) {
        _observerList.Add(obs);
    }
    
    private void notify(int maschineId) {
        foreach(Observer obs in _observerList) {
            obs.update(maschineId);
        }
    }
}
   
public class Observer {
    public void update(int maschineId) {
        DateTime localDate = DateTime.Now;
        Console.WriteLine($"Defect bulb identified on maschine {maschineId} at {localDate}.");
    }
}
Palle Due
  • 5,929
  • 4
  • 17
  • 32
Z.J
  • 301
  • 1
  • 7
  • I do not see anything "CPU bound" in the given example. CPU bound usually means that you are doing actual computations, not waiting for a timer to elapse. – JonasH Mar 29 '23 at 12:45
  • @JonasH isn't `Task.Delay()` a sufficient simulation of CPU-bound work? – Good Night Nerd Pride Mar 29 '23 at 13:10
  • @GoodNightNerdPride it depends. But it is better simulation of IO-bound work =) – Guru Stron Mar 29 '23 at 13:13
  • 1
    @GoodNightNerdPride No absolutely not. It does nothing at all, it just parks the task until the timer is up. To simulate CPU you would need a busy wait such as `while (true)` – Charlieface Mar 29 '23 at 13:34
  • As a side note, names of methods in C# [are Pascal-cased](https://github.com/ktaranov/naming-convention/blob/master/C%23%20Coding%20Standards%20and%20Naming%20Conventions.md). `RegisterObserver`, `Notify` and `Update` is correct. – Theodor Zoulias Mar 29 '23 at 13:51
  • Using an asynchronous call like `Task.Delay` to simulate CPU work risks hiding some potential problems. It is not really an issue if you await a hundred `Task.Delay` concurrently, each will only consume a small amount of memory. But you most likely do not have a hundred CPU cores, so you cannot do that much actual work concurrently. – JonasH Mar 29 '23 at 14:07

1 Answers1

2

await will stop execution of current method and will return control to the caller one. Since prod.startMaschine(maschineID); is part of the Main method it will block the while(!input.Equals("exit")) execution until Task returned by prod.startMaschine(maschineID) is finished (i.e. all 10000 iterations in the Task.Run).

Simple repro would look like:

while (!input.Equals("exit"))
{
    input = Console.ReadLine();
    switch (input)
    {
        case "1":
            Console.WriteLine("Before await");
            await Task.Delay(10000); // will wait 10 second before printing next line 
            Console.WriteLine("After await");
            Console.WriteLine(maschineID);
            maschineID++;
            Console.WriteLine("----");
            break;
        case "2":
            input = "exit";
            break;
        default: break;
    }
}

The original prod.startMaschine(maschineID); call on the other hand is a so called fire-and-forget call.

Some potentially useful links:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132