6
\$\begingroup\$

It's been quite some time since I got my eye on the multi-threading, today I decided to create a really simple application which runs 2 while loops simultaneously and prints the current progress of the thread to the console window, each thread's run time is taken as an input from the user i.e one thread can run for 5 secs the other one can run for 10. I'm quite unhappy with how it looks but since I cant seem to use methods with parameters, I'm kinda stuck. Any improvements considering the code style, performance and overall design are welcome.

    private static int timeToRunFirstThread;
    private static int timeToRunSecondThread;
    private static Thread firstThread = new Thread(FirstThreadFunction);
    private static Thread secondThread = new Thread(SecondThreadFunction);
    private static Stopwatch sw;
    private static void Main(string[] args)
    {
        timeToRunFirstThread = int.Parse(Console.ReadLine());
        timeToRunSecondThread = int.Parse(Console.ReadLine());
        timeToRunFirstThread *= 1000;
        timeToRunSecondThread *= 1000;
        sw = Stopwatch.StartNew();
        firstThread.Start();
        secondThread.Start();
        Thread firstThreadChecker = new Thread(FirstThreadCheckerFunction);
        Thread secondThreadChecker = new Thread(SecondThreadCheckerFunction);
        firstThreadChecker.Start();
        secondThreadChecker.Start();
        Console.ReadKey();
    }

    private static void FirstThreadCheckerFunction()
    {
        while (firstThread.IsAlive)
        {
            WriteThreadProgressToScreen("Thread #1", timeToRunFirstThread, sw.ElapsedMilliseconds);
            Thread.Sleep(100);
        }
        Console.WriteLine("Thread #1 Finished !");
    }

    private static void SecondThreadCheckerFunction()
    {
        while (secondThread.IsAlive)
        {
            WriteThreadProgressToScreen("Thread #2", timeToRunSecondThread, sw.ElapsedMilliseconds);
            Thread.Sleep(100);
        }
        Console.WriteLine("Thread #2 Finished !");
    }

    private static void FirstThreadFunction()
    {
        Thread.Sleep(timeToRunFirstThread);
    }

    private static void SecondThreadFunction()
    {
        Thread.Sleep(timeToRunSecondThread);
    }

    private static void WriteThreadProgressToScreen(string threadName, int timeToRunThread, long currentElapsedMiliseconds)
    {
        Console.WriteLine($"{threadName} progress : {Math.Round(currentElapsedMiliseconds / (double)timeToRunThread * 100, 1)}%");
    }

And that's my second approach where I tried to reduce the threads as much as possible and also the repetitive code :

public class Program
{
    private static int timeToRunFirstThread;
    private static int timeToRunSecondThread;
    private static Thread firstThread = new Thread(ThreadChecker);
    private static Thread secondThread = new Thread(ThreadChecker);
    private static Stopwatch sw;
    private static void Main(string[] args)
    {
        timeToRunFirstThread = int.Parse(Console.ReadLine());
        timeToRunSecondThread = int.Parse(Console.ReadLine());
        timeToRunFirstThread *= 1000;
        timeToRunSecondThread *= 1000;
        sw = Stopwatch.StartNew();
        firstThread.Start(new ThreadParameters("Thread #1", timeToRunFirstThread, firstThread));
        secondThread.Start(new ThreadParameters("Thread #2", timeToRunSecondThread, secondThread));
        Console.ReadKey();
    }

    private static void ThreadChecker(object input)
    {
        ThreadParameters threadParameters = (ThreadParameters)input;
        while (sw.ElapsedMilliseconds < threadParameters.TimeToRun)
        {
            Console.WriteLine($"{threadParameters.Name} progress : {Math.Round(sw.ElapsedMilliseconds / (double)threadParameters.TimeToRun * 100, 1)}%");
            Thread.Sleep(100);
        }
        Console.WriteLine($"{threadParameters.Name} Finished !");
    }
}

public class ThreadParameters
{
    public string Name { get; private set; }
    public int TimeToRun { get; private set; }
    public Thread CurrentThread { get; private set; }

    public ThreadParameters(string name, int timeToRun, Thread currentThread)
    {
        Name = name;
        TimeToRun = timeToRun;
        CurrentThread = currentThread;
    }
}
\$\endgroup\$
4
\$\begingroup\$

It feels a bit like you might be looking for how to start a parameterised thread. In the code below, I've adapted your code slightly so that ThreadChecker is aware of what type to expect. This is initialized when the thread is created, which means that the parameters can't pass the thread object as part of ThreadParameters, however this isn't being used currently anyway and could be retrieved through Thread.CurrentThread if required.

public class Program
{
    private static int timeToRunFirstThread;
    private static int timeToRunSecondThread;
    private static Stopwatch sw;
    private static void Main(string[] args)
    {
        timeToRunFirstThread = int.Parse(Console.ReadLine());
        timeToRunSecondThread = int.Parse(Console.ReadLine());
        timeToRunFirstThread *= 1000;
        timeToRunSecondThread *= 1000;
        sw = Stopwatch.StartNew();
        Thread firstThread = new Thread(() => ThreadChecker(new ThreadParameters("Thread #1", timeToRunFirstThread)));
        Thread secondThread = new Thread(() => ThreadChecker(new ThreadParameters("Thread #2", timeToRunFirstThread)));
        firstThread.Start();
        secondThread.Start();
        Console.ReadKey();
    }

    private static void ThreadChecker(ThreadParameters threadParameters)
    {
        while (sw.ElapsedMilliseconds < threadParameters.TimeToRun)
        {
            Console.WriteLine($"{threadParameters.Name} progress : {Math.Round(sw.ElapsedMilliseconds / (double)threadParameters.TimeToRun * 100, 1)}%");
            Thread.Sleep(100);
        }
        Console.WriteLine($"{threadParameters.Name} Finished !");        
    }
}

public class ThreadParameters
{
    public string Name { get; private set; }
    public int TimeToRun { get; private set; }

    public ThreadParameters(string name, int timeToRun)
    {
        Name = name;
        TimeToRun = timeToRun;
    }
}
\$\endgroup\$
5
\$\begingroup\$

Wouldn't it be easier to use new async/await?

  • You can pass parameters without helper objects
  • You can wait until all tasks are finished
  • You can easy create as many tasks as you want
  • You can display % as {progress:p} which makes the % sign and the Math.Round unnecessary

For example:

private static void Main(string[] args)
{
    RunFoos(3, 5);
}

static void RunFoos(params int[] workloads)
{
    var tasks = workloads.Select((w, i) => Task.Factory.StartNew(() => Foo(w, i)));
    Task.WaitAll(tasks.ToArray());
}

static Task Foo(int workload, int taskNumber)
{
    Console.WriteLine($"Trhead: #{Thread.CurrentThread.ManagedThreadId, 2} Task: {taskNumber} working for {workload} sec...");
    var sw = Stopwatch.StartNew();
    while (sw.Elapsed.TotalMilliseconds < workload * 1000)
    {
        var progress = sw.ElapsedMilliseconds / ((double)workload * 1000);
        Console.WriteLine($"Trhead: #{Thread.CurrentThread.ManagedThreadId} Task:{taskNumber} progress : {progress:p}");
        Thread.Sleep(200);
    }
    Console.WriteLine($"Trhead #{Thread.CurrentThread.ManagedThreadId} done!");
    return Task.FromResult<object>(null);
}
\$\endgroup\$
  • \$\begingroup\$ Thank you for the answer, however I'm trying to learn multi-threading with C# so I really don't want the answer to propose alternative approaches, even tho they might be the better way to go. Again I'm trying to practice multi-threading with some random application I come up with, they are not used in a real situation. \$\endgroup\$ – Denis Jul 31 '16 at 17:43

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.