-1

I want the below process run continuously.But confused to use Thread or Task. I am also new in implementing Thread or Task.Is the process is right I am implementing? Between Thread and Task which is better for getting fast performance for long running process?

private void BtnStart_Click(object sender, EventArgs e)
    {
        IClockThread();

    }

This method is creating thread for every machine.

  public void IClockThread()
    {

        txtStatus.BeginInvoke((Action)(() => txtStatus.Text = "Thread for IClock Starts......" + Environment.NewLine));

        Thread[] ts = new Thread[IclockDetails.Count];
        for (int i = 0; i < 5; i++)
        {
            string IP = IclockDetails[i].IpAddress;
            ts[i] = new Thread(() =>
              {
                  ConnectMachineIClock(IP);
              });
            ts[i].Start();

        }
        txtStatus.BeginInvoke((Action)(() => txtStatus.Text += "Thread for IClock Ends............" + Environment.NewLine));
 }

This is another method which is called by every thread.

    public void ConnectMachineIClock(string IP)
    {
        int idwErrorCode = 0;
        var Iclock = IclockDetails.Where(a => a.IpAddress == IP).FirstOrDefault();

        if (AttnMachineRepository.IsMachineOnline(IP) == true)
        {


            Stopwatch sw = Stopwatch.StartNew();
            blnCon = CZKEM1.Connect_Net(IP.Trim(), 4370);
            sw.Stop();
            if (blnCon == true)
            {
                UpdateText(1, txtStatus, Iclock.IpAddress);
                iMachineNumber = Iclock.Id;
                LocId = Iclock.LocationId;
                MType = Iclock.MachineTypeId;
                LocName = AttnMachineRepository.GetPunchLocation(LocId);
                CZKEM1.RegEvent(iMachineNumber, 65535);
                UpdateText(2, txtStatus, Iclock.IpAddress);
                //txtStatus.BeginInvoke((Action)(() => txtStatus.Text += ("Connected with " + Iclock.IpAddress + " " + sw.Elapsed.TotalSeconds + " Seconds taken to connect") + Environment.NewLine));
                MachineIP = Iclock.IpAddress;
                Get_IClock_LogData(iMachineNumber);

            }
            else
            {
                CZKEM1.GetLastError(ref idwErrorCode);
                UpdateText(-1, txtErrorLog, Iclock.IpAddress);
                //txtErrorLog.BeginInvoke((Action)(() => txtErrorLog.Text += "Unable to connect the device with IP: " + MachineIP + ", ErrorCode = " + idwErrorCode.ToString() + "" + Environment.NewLine));
                //Application.DoEvents();
            }
        }
        else
        {
            UpdateText(-2, txtErrorLog, Iclock.IpAddress);
            //txtErrorLog.BeginInvoke((Action)(() => txtErrorLog.Text += "IP " + MachineIP + " not found" + Environment.NewLine));
            //Application.DoEvents();
        }
    }
      public void UpdateText(int status, TextBox text, string IP)
    {
        switch (status)
        {
            case 1:
                text.BeginInvoke((Action)(() => text.Text += ("Data Processing for" + IP + " starts") + Environment.NewLine));
                Application.DoEvents();
                break;
            case 2:
                text.BeginInvoke((Action)(() => text.Text += ("Connected with " + IP) + Environment.NewLine));
                Application.DoEvents();
                break;
            case -1:
                text.BeginInvoke((Action)(() => text.Text += "Unable to connect the device with IP: " + IP + ", ErrorCode = " + -1 + "" + Environment.NewLine));
                Application.DoEvents();
                break;
            case -2:
                text.BeginInvoke((Action)(() => text.Text += "IP " + IP + " not found" + Environment.NewLine));
                Application.DoEvents();
                break;

        }
    }
Syed Islam
  • 73
  • 1
  • 11
  • How long will the process be running? It is not clear from the code how ling some of the function calls will take. In general I would use async and await so all the explicit switching to the UI thread is not needed – Emond Apr 04 '18 at 04:22
  • actually this is a process to download data from finger machine. so it has no time limit. it runs continuously until any one stops. – Syed Islam Apr 04 '18 at 04:29

2 Answers2

1

Analysis

So your current code basically spins up a new thread for each clock detail, in order to basically have those calls all run asynchronously. It doesn't await for any threads to finish.

The first problem with that is creating new threads is expensive, and running 100 tasks on 100 threads is very rarely, if ever, as efficient as say 4 threads running in parallel taking care of tasks as they become available.

So the first issue is that creating a bunch of new threads is not the best approach here.

Secondly, hitting up the network stack with 100 threads (or however many are in IClockDetails) is also less efficient than fewer concurrent threads.

Difference Between Task and Thread

To directly answer your first question, it is better to use Task's in this case. Also the only difference you will get from doing

new Thread(() => {}).Start();

And doing

Task.Run(() => {});

Is that the first way will always create a new thread (expensive) to do the work on, whereas the second will possibly use an existing, idle thread.

Secondly, a Task always creates a background thread, not foreground, so the Task's thread won't keep your application alive if your main and all foreground threads finish, whereas a foreground thread will.

That is it. Your code with the exception of possibly being run on an existing idle thread, and it being run as a background thread, there is no difference at all.

Standard Solution

As mentioned, creating a new thread is expensive. So the first and simple answer to your question is, it is much better to change your new Thread(() => {}).Start(); calls to Task.Run(() => {}); calls.

public void IClockThread()
{
    txtStatus.BeginInvoke((Action)(() => txtStatus.Text = "Thread for IClock Starts......" + Environment.NewLine));

    for (int i = 0; i < IclockDetails.Length; i++)
        Task.Run(() => ConnectMachineIClock(IclockDetails[i].IpAddress));

    txtStatus.BeginInvoke((Action)(() => txtStatus.Text += "Thread for IClock Ends............" + Environment.NewLine));
}

I have cleaned up the code, fixed your for loop to use the actual IclockDetails length, removed unnecessary bits, and replaced the thread with the Task.

This will now re-use threads as they become available, and run them as background threads.

Parallel to the rescue

As mentioned though, if your IclockDetails has 100 items or anything more than say 20, its inefficient to just run threads for each item. Instead you could parallel them up.

Consider this

Parallel.For(0, 100, (i) =>
{
    Console.WriteLine($"I am {i} on thread {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(10 * i);
});

This will create a bunch of methods which will get called in parallel as the Partitioner sees fit. So this will run based on the most optimal thread count for the system its running on

It will just output to the console the i of which work item it is between the range I specified of 0 to 100. It delays more the larger the number. So if you run this code now you will see a bunch of output like this

I am 0 on thread 1
I am 1 on thread 1
I am 2 on thread 1
I am 5 on thread 3
I am 10 on thread 4
I am 3 on thread 1
I am 15 on thread 5
I am 6 on thread 3
I am 4 on thread 1
I am 20 on thread 6
I am 25 on thread 7
I am 11 on thread 4
I am 35 on thread 11
I am 8 on thread 1
I am 30 on thread 8
I am 45 on thread 12
I am 7 on thread 3
I am 40 on thread 10
I am 50 on thread 9
I am 55 on thread 14
I am 60 on thread 15
I am 16 on thread 13
I am 65 on thread 5
I am 70 on thread 16
I am 75 on thread 18
I am 9 on thread 1
...

As you can see the run order is out of sync as they are run in parallel and the threads are getting re-used.

You can limit the max number of parallel tasks running at once if you like with

Parallel.For(0, 100, new ParallelOptions { MaxDegreeOfParallelism = 4 }...

To limit it to 4 in this example. However, I would personally leave it up to the Paritioner to make that decision.

For clear understanding, if you limit the MaxDegreeOfParallelism = 1 your output would be

I am 0 on thread 1
I am 1 on thread 1
I am 2 on thread 1
I am 3 on thread 1
I am 4 on thread 1
I am 5 on thread 1
I am 6 on thread 1
I am 7 on thread 1
I am 8 on thread 1
I am 9 on thread 1
I am 10 on thread 1
...

NOTE: The Parallel.For call does not finish and carry on to the next line of code until all the work is done. If you want to change that just do Task.Run(() => Parallel.For(...));

Best Solution

So with that in mind, my proposal for your situation and the best solution is to use Parallel.For to split up your work and delegate it onto the right threads and make re-use of those threads as the system see's fit.

Also note I only use Task.Run here to maintain your Thread for clock start/end outputs so they act exactly the same and your IClockThread() method returns before the work is done, so as to not change your current code flow.

public void IClockThread()
{

    txtStatus.BeginInvoke((Action)(() => txtStatus.Text = "Thread for IClock Starts......" + Environment.NewLine));

    Task.Run(() =>
    {
        Parallel.For(0, IclockDetails.Count, (i) =>
        {
            ConnectMachineIClock(IclockDetails[i].IpAddress);
        });
    });

    txtStatus.BeginInvoke((Action)(() => txtStatus.Text += "Thread for IClock Ends............" + Environment.NewLine));
}
angelsix
  • 392
  • 1
  • 7
-1

The main difference between a Task and a Thread is how the concurrency is done.

A Task is concurrency that is delegated to a thread in the application's thread pool. The idea is that a Task is a reasonably short lived, concurrent procedure or function, that is handed off to a thread in the thread pool where it is executed and finished and then the thread being used is returned back to the thread pool for some other task.

See the MSDN documentation Task Class provides an overview with links to various method descriptions, etc.

The Task class represents a single operation that does not return a value and that usually executes asynchronously. Task objects are one of the central components of the task-based asynchronous pattern first introduced in the .NET Framework 4. Because the work performed by a Task object typically executes asynchronously on a thread pool thread rather than synchronously on the main application thread, you can use the Status property, as well as the IsCanceled, IsCompleted, and IsFaulted properties, to determine the state of a task. Most commonly, a lambda expression is used to specify the work that the task is to perform.

For operations that return values, you use the Task < TResult > class.

Also see Microsoft Docs Task-based Asynchronous Programming as well as Task-based Asynchronous Pattern (TAP) in Microsoft Docs which is the current recommended approach (see Asynchronous Programming Patterns for a discussion in Microsoft Docs on several patterns).

This MSDN article Asynchronous programming describes using the async and await keywords with links to additional articles including the Microsoft Docs article Async in depth which gets into the gory details.

A Thread is concurrency that is created by the application. The idea is that a Thread is a reasonably long lived, concurrent procedure or function.

A major difference between the two, Task and Thread, is that a Task is handed off to a ready to run thread while a Thread has to be created and spun up. So the startup time is lower and startup efficiency is higher for a Task as the thread it is delegated to already exists.

In my opinion the primary reason to use a Task is to be able to make a short lived action or procedure concurrent. So things such as accessing a web site or doing some kind of a data transfer or performing a calculation of some kind that requires several seconds or updating a data store are the ideal types of activities for a Task. There are a large number of Async type functions with .NET and C# and C++/CLI that are designed to be used with Task to trigger activities while allowing the UI to remain responsive.

For a Thread, activities such as providing a server that accepts a high volume of requests and acts on them or a function that is monitoring several devices or sensors for a long period would be ideal types of activities for a Thread. Other activities would be additional UI threads in order to handle a complex User Interface or compute intensive tasks such as graphics rendering whose throughput would be enhanced by being able to use one or more dedicated CPU cores.

One point to remember is that Task versus Thread is not a binary, either one or the other decision. One or more Threads may be created to handle particular, encapsulated and self sufficient functionality and within one or more of the Threads created the Task construct may be used to handle the work of a Thread. A common example is the UI thread which is designed to handle the various messages of a User Interface however particular actions that happen within the UI thread are handled by a Task. Another common example would be a server thread which handle multiple concurrent connections using Tasks.

The Microsoft Docs article, The Managed Thread Pool, has this to say:

There are several scenarios in which it is appropriate to create and manage your own threads instead of using thread pool threads:

  • You require a foreground thread.

  • You require a thread to have a particular priority.

  • You have tasks that cause the thread to block for long periods of time. The thread pool has a maximum number of threads, so a large number of blocked thread pool threads might prevent tasks from starting.

  • You need to place threads into a single-threaded apartment. All ThreadPool threads are in the multithreaded apartment.

  • You need to have a stable identity associated with the thread, or to dedicate a thread to a task.

In addition see the following stackoverflow postings and discussions about the differences between Task and Thread.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • This is just all wrong. A `Task` represents an *asynchronous* operation. It doesn't (necessarily) represent work done in another thread. It doesn't need to be short at all. It's entirely appropriate for a task to represent an asynchronous operation that will take a very long time. Tasks also don't necessarily have anything to do with concurrency. You can, and it's often appropriate to, use tasks to handle asynchronous operations that *never do work in parallel*. – Servy Apr 06 '18 at 14:44
  • "For a Thread, activities such as providing a server that accepts requests and acts on them or a function that is monitoring some device or sensor for a long period would be ideal types of activities for a Thread." None of that is appropriate work for a thread. That's all IO bound work, not CPU bound work. There's no reason for *any* thread to be involved in doing such work. `Task` is of course specifically designed to be able to represent such asynchronous work that doesn't utilize any threads to do said work. – Servy Apr 06 '18 at 14:45
  • You should read your own links at the end, as they contradict a large portion of what your answer says. – Servy Apr 06 '18 at 14:46
  • Tasks ultimately just use Threads. They just make the thread always Background not Foreground with that main difference being foreground threads keep the application alive until done whereas background threads do not. Also Tasks handle which threads to use based on multiple factors and threads if you make one is a direct creation of a new thread. Agreed this post though is mostly wrong – angelsix Apr 07 '18 at 22:34
  • @angelsix No, again, that's not true. A `Task` represents *an asynchronous operation*. One type of asynchronous operation is running a delegate in a background thread, but that's just one of many possibilities. A task could represent any number of other possibilities. – Servy Apr 09 '18 at 13:50
  • A Task does all its work on a thread nothing else but a thread is what ultimately runs the work. So what I said is correct – angelsix Apr 09 '18 at 13:55
  • @angelsix while threads are the underlying mechanism, @Servy is correct that a `Task` represents an asynchronous operation, also the word concurrent operation is used in the documentation and commentary, and the asynchronous operation may not be done on a background thread. A foreground thread may be chosen especially when the `Task` is a coroutine on the UI thread and a standard Async Windows library routine is being used for the operation. – Richard Chambers Apr 09 '18 at 14:50
  • @angelsix That's just demonstrably false. A task can represent IO work that *doesn't involve any code running on a thread at all*, a task could represent an aggregation of other tasks, or for the user to provide some input value. None of these things involve a thread running any work. Given that you've already been provided concrete examples of tasks that represent actions that don't involve using threads at all, its not meaningful to just assert that all tasks represent work run on a thread. – Servy Apr 09 '18 at 14:51
  • @RichardChambers No, again, you are incorrect to say that threads are the underlying mechanism. They're *one possible* underlying mechanism of many. *Lots* of tasks involve no additional threads at all. Some do, sure, but it's neither inherent to tasks in general, nor used in all tasks. – Servy Apr 09 '18 at 14:52
  • @Servy please provide a reasonably reputable source for what you are saying that provides a more indepth discussion with details. Everything that I have found thus far indicates there is an underlying thread whether that thread is a user created thread, a thread from the system pool, or a thread within a kernel process. – Richard Chambers Apr 09 '18 at 14:56
  • @RichardChambers I don't have to, *you provided them yourself* right in your own answer. All of the sources you've linked here take care to indicate that a task can represent more than just some code running on a thread. I challenge you to provide even a *single* [reputable] source that indicates that a task can only ever represent code running on a thread, and that it can't represent anything else. – Servy Apr 09 '18 at 15:00
  • @Servy ok please enlighten me (I'm not trying to dig or say who is right or wrong, I am genuinly unaware). Please show me how to use a Task that doesn't run work on a Thread, and is of any use to anything? I ask because all code in a program runs on a thread. So to say a task does not run on a thread means it cannot even execute a single line of compiled code – angelsix Apr 09 '18 at 15:36
  • And for all interested in why I say Tasks... https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/Tasks/Task.cs#L538 Run a task (after all, it doesn't do anything if you don't run it and is unused) https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/Tasks/Task.cs#L5164 Which calls InternalStartNew https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/Tasks/Task.cs#L1184 Which calls InternalQueueTask https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/Tasks/Task.cs#L1766 – angelsix Apr 09 '18 at 15:38
  • Which fires QueueTask of the TaskScheduler https://github.com/dotnet/coreclr/blob/da8b7181dbe362fbfe64c7ef72ff3a4a7db68f24/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs#L266 Which calls the Post of SynchronizationContext https://github.com/dotnet/coreclr/blob/da8b7181dbe362fbfe64c7ef72ff3a4a7db68f24/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs#L651 Which calls... I wonder... ThreadPool... https://github.com/dotnet/coreclr/blob/4b95b41d93e7b267c972fe60e0fe9f2068cbeb1c/src/mscorlib/src/System/Threading/SynchronizationContext.cs#L111 – angelsix Apr 09 '18 at 15:38
  • @angelsix I already gave you a bunch of examples. Doing network IO doesn't involve work being done on a thread, a task that represents an aggregation of other tasks, or that is composed of other tasks, doesn't represent code running on a thread, a task could represent the completion of some user input, etc. You've also got tasks like `Task.Delay` that represent a task that completes in a fixed period of time, which doesn't involve running code on a thread. Yes, `Task.Run` will run code on a background thread; that's its job, but not all tasks are created via `Task.Run`. – Servy Apr 09 '18 at 15:59
  • You really need to provide code before you provide invalid answers. You do know network IO works on threads? You know a task that represents other tasks has to process information (reading the tasks) from other tasks... which happens on the thread it was created, and I know you certainly did your research before posting that Task.Delay doesn't use a thread right? The open source must be wrong. Delay uses a DelayPromise which is a Task which makes use of TimerQueueTimer which uses callbacks on a thread https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/threading/timer.cs – angelsix Apr 09 '18 at 16:11
  • And to clarify as always, network I/O and Tasks for other Tasks both use a sync context of sorts to ask the underlying network device or other Task to call its _thread_ once anything is done or happens, and in between that time no CPU time is used. However the Task is still created on, observed and used on a thread. So no work, no checking the task status (whether network IO or other), no functional, observable or actionable action about any Task at all in C# can happen without being on a thread. – angelsix Apr 09 '18 at 16:25
  • @angelsix The fact that a tiny bit of code runs on a thread to *start* a task, and that a tiny bit of code needs to run on a thread to mark it as completed when it's finished is of course true, but doesn't mean that all tasks are inherently representations of work done on another thread. The actual operation that the `Task` represents is any arbitrary asynchronous operation, many of which don't involve the use of any threads at all. – Servy Apr 09 '18 at 17:33
  • That a few lines of code (that is such a miniscule portion of the actual work done it can typically be ignored entirely) need to run to start and complete the task *while the vast majority of the actual work uses no threads to do anything* doesn't change that. Saying that all tasks only represent running work in a thread, as you have said repeatedly, is both false, and extremely harmful to say as it greatly impedes people's understanding of the TPL and their ability to use it properly. – Servy Apr 09 '18 at 17:33
  • Ok I see your side of the discussion also, and can say that the point of some Tasks is to represent work done that is not involving CPU bound Threads on the running program, is a valid point such as Task.Delay is designed to not tie up any CPU work in our program. However, I did not say that at the start and never intended to describe the "representation" or "intention" of a Task. Here is what I said. "Tasks ultimately just use Threads.". They use threads. That is the point I was clearing up. More to the point, the OPs question, the one meant to be answered, involves thread-bound work – angelsix Apr 09 '18 at 17:42
  • @angelsix You were *very* specific in your earlier comments, "A Task does all its work on a thread nothing else but a thread is what ultimately runs the work." That is not saying that a task may do all of it's meaningful work without using any threads, but a small bit of work needs to be run in a thread to start and complete a task. You were *very* explicit in saying that a task can *only* represent executing code in another thread, and nothing else, and that's just not true. As to the OP's specific situation, the OP's code involves network communications, not running code in a thread. – Servy Apr 09 '18 at 17:51
  • Ok I think we can just agree to disagree as this will go on forever. – angelsix Apr 09 '18 at 18:02