Even if I am using TPL from long time but since it sounds new to me. I want to understand the TPL with thread pool and I created a POC in .NET framework 4.0 for that which is as below.
public class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class TPLSample
{
public int MaxThread = 0;
public void Start()
{
Task[] taskArray = new Task[10000];
for (int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = Task.Factory.StartNew((Object obj) =>
{
var data = new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks };
Thread.SpinWait(10000);
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
if (Thread.CurrentThread.ManagedThreadId > MaxThread)
{
MaxThread = Thread.CurrentThread.ManagedThreadId;
}
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
},
i);
}
Task.WaitAll(taskArray);
Console.WriteLine("Max no of threads {0}", MaxThread);
}
}
I found that only 14 threads are created to do this task!!
But why the 14? what is the criteria ? can I increase or decrease this number?
How can I change this number. Is it really possible or totally abstracted from a developer.
From MSDN:
The number of operations that can be queued to the thread pool is limited only by available memory; however, the thread pool limits the number of threads that can be active in the process simultaneously. Beginning with the .NET Framework 4, the default size of the thread pool for a process depends on several factors, such as the size of the virtual address space. A process can call the GetMaxThreads method to determine the number of threads.
Another MSDN:
The TPL may employ various optimizations, especially with large numbers of delegates.
Another SO question about this. Hopefully this will quench your thirst.
Related
I've made a clean project of .net Core 3.0 Web application and I am trying to understand, how ThreadPool works in C#.
namespace TestASPSelf.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public static int countThread = 0;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
int workerThreads;
int portThreads;
ThreadPool.GetMaxThreads(out workerThreads, out portThreads);
Console.WriteLine("\nMaximum worker threads: \t{0}" +
"\nMaximum completion port threads: {1}",
workerThreads, portThreads);
ThreadPool.GetAvailableThreads(out workerThreads,
out portThreads);
Console.WriteLine("\nAvailable worker threads: \t{0}" +
"\nAvailable completion port threads: {1}\n",
workerThreads, portThreads);
Console.WriteLine("countThread = " + countThread);
return View();
}
class Z
{
public static void WaitTest(object o)
{
countThread++;
while (true)
{
Thread.Sleep(1000);
}
}
}
public IActionResult Privacy()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("starting thread "+i);
ThreadPool.QueueUserWorkItem(new WaitCallback(Z.WaitTest));
}
return View();
}
}
}
When http://localhost:5000/Home/Privacy opened, it hangs for some time (for about 40-80 seconds), but I see, that logic of for cycles in it completes almost instantly.
When http://localhost:5000/ opened after that, it hangs for 40-80 seconds too and result is in console countThread = 100.
CPU usage of app is about 5-10%, when threads were started.
I am trying to understand:
1) The first one is why ASP controller hangs for 40-80 seconds per page, when 100 Threads are running by CPU usage 5-10 percents. CPU has a lot of resources, RAM is free too, but why ASP Controller methods of pages are hangs?
2) How to create ThreadPool in C# with limited count of running threads? If I understand method public static bool SetMinThreads (int workerThreads, int completionPortThreads); correctly, it affects globally all threads of the app. How to create object of ThreadPool with limited count of active threads, like ExecutorService in Java? For example, Java code of thread pool could look like
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
3) How to prevent hangs of all methods of ASP Controller and to make "truly real" threads, like in Java?
ThreadPool.QueueUserWorkItem(new WaitCallback(Z.WaitTest));
With this you do something very wrong. You cause threads in the threadpool to block and thus the pool is unable to finish processing your request and process new requests.
At some point a thread from the pool manages to return and process your next request, but than it hangs again due to the overloaded pool.
As for your other questions, please explain what you want to achieve. Your question seems to try to solve a problem that is not understood well.
Update: After Arthur's comment.
If you will be downloading files, you should use Tasks and async-await. IO operations do not consume threads (more here).
Create N tasks, each downloading a file, and then await Task.WhenAll. Pseudo code:
List<Task> tasks = new List<Task>();
for (int i = 0; i < filesToDownloadCount; i++)
{
var t = new Task ( () => { /* ... code to download your file here ... */});
tasks.Add (t);
}
await t.WhenAll (tasks);
This approach will give you the best throughput and your bottleneck will be your bandwidth, not the CPU.
The ThreadPool class has several static methods including the QueueUserWorkItem that is responsible for calling a thread pool worker thread when it is available. If no worker thread is available in the thread pool, it waits until the thread becomes available.
using System;
using System.Threading;
class ThreadPoolSample
{
// Background task
static void BackgroundTask(Object stateInfo)
{
Console.WriteLine("Hello! I'm a worker from ThreadPool");
Thread.Sleep(1000);
}
static void BackgroundTaskWithObject(Object stateInfo)
{
Person data = (Person)stateInfo;
Console.WriteLine($"Hi {data.Name} from ThreadPool.");
Thread.Sleep(1000);
}
static void Main(string[] args)
{
// Use ThreadPool for a worker thread
ThreadPool.QueueUserWorkItem(BackgroundTask);
Console.WriteLine("Main thread does some work, then sleeps.");
Thread.Sleep(500);
// Create an object and pass it to ThreadPool worker thread
Person p = new Person("Mahesh Chand", 40, "Male");
ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);
int workers, ports;
// Get maximum number of threads
ThreadPool.GetMaxThreads(out workers, out ports);
Console.WriteLine($"Maximum worker threads: {workers} ");
Console.WriteLine($"Maximum completion port threads: {ports}");
// Get available threads
ThreadPool.GetAvailableThreads(out workers, out ports);
Console.WriteLine($"Availalbe worker threads: {workers} ");
Console.WriteLine($"Available completion port threads: {ports}");
// Set minimum threads
int minWorker, minIOC;
ThreadPool.GetMinThreads(out minWorker, out minIOC);
ThreadPool.SetMinThreads(4, minIOC);
// Get total number of processes availalbe on the machine
int processCount = Environment.ProcessorCount;
Console.WriteLine($"No. of processes available on the system: {processCount}");
// Get minimum number of threads
ThreadPool.GetMinThreads(out workers, out ports);
Console.WriteLine($"Minimum worker threads: {workers} ");
Console.WriteLine($"Minimum completion port threads: {ports}");
Console.ReadKey();
}
// Create a Person class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public Person(string name, int age, string sex)
{
this.Name = name;
this.Age = age;
this.Sex = sex;
}
}
}
This is an example about Thread Local Storage (TLS) from Apress parallel programming book. I know that if we have 4 cores computer 4 thread can run parallel in same time. In this example we create 10 task and we suppose that have 4 cores computer. Each Thread local storage live in on thread so when start 10 task parallel only 4 thread perform. And We have 4 TLS so 10 task try to change 4 Thread local storage object. i want to ask how Tls prevent data race problem when thread count < Task count ??
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Listing_04
{
class BankAccount
{
public int Balance
{
get;
set;
}
}
class Listing_04
{
static void Main(string[] args)
{
// create the bank account instance
BankAccount account = new BankAccount();
// create an array of tasks
Task<int>[] tasks = new Task<int>[10];
// create the thread local storage
ThreadLocal<int> tls = new ThreadLocal<int>();
for (int i = 0; i < 10; i++)
{
// create a new task
tasks[i] = new Task<int>((stateObject) =>
{
// get the state object and use it
// to set the TLS data
tls.Value = (int)stateObject;
// enter a loop for 1000 balance updates
for (int j = 0; j < 1000; j++)
{
// update the TLS balance
tls.Value++;
}
// return the updated balance
return tls.Value;
}, account.Balance);
// start the new task
tasks[i].Start();
}
// get the result from each task and add it to
// the balance
for (int i = 0; i < 10; i++)
{
account.Balance += tasks[i].Result;
}
// write out the counter value
Console.WriteLine("Expected value {0}, Balance: {1}",
10000, account.Balance);
// wait for input before exiting
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}
}
}
We have 4 TLS so 10 task try to change 4 Thread local storage object
In your example, you could have anywhere between 1 and 10 TLS slots. This is because a) you are not managing your threads explicitly and so the tasks are executed using the thread pool, and b) the thread pool creates and destroys threads over time according to demand.
A loop of only 1000 iterations will completely almost instantaneously. So it's likely all ten of your tasks will get through the thread pool before the thread pool decides a work item has been waiting long enough to justify adding any new threads. But there is no guarantee of this.
Some important parts of the documentation include these statements:
By default, the minimum number of threads is set to the number of processors on a system
and
When demand is low, the actual number of thread pool threads can fall below the minimum values.
In other words, on your four-core system, the default minimum number of threads is four, but the actual number of threads active in the thread pool could in fact be less than that. And if the tasks take long enough to execute, the number of active threads could rise above that.
The biggest thing to keep in mind here is that using TLS in the context of a thread pool is almost certainly the wrong thing to do.
You use TLS when you have control over the threads, and you want a thread to be able to maintain some data private or unique to that thread. That's the opposite of what happens when you are using the thread pool. Even in the simplest case, multiple tasks can use the same thread, and so would wind up sharing TLS. And in more complicated scenarios, such as when using await, a single task could wind up executed in different threads, and so that one task could wind up using different TLS values depending on what thread is assigned to that task at that moment.
how Tls prevent data race problem when thread count < Task count ??
That depends on what "data race problem" you're talking about.
The fact is, the code you posted is filled with problems that are at the very least odd, if not outright wrong. For example, you are passing account.Balance as the initial value for each task. But why? This value is evaluated when you create the task, before it could ever be modified later, so what's the point of passing it?
And if you thought you were passing whatever the current value is when the task starts, that seems like that would be wrong too. Why would it be valid to make the starting value for a given task vary according to how many tasks had already completed and been accounted for in your later loop? (To be clear: that's not what's happening…but even if it were, it'd be a strange thing to do.)
Beyond all that, it's not clear what you thought using TLS here would accomplish anyway. When each task starts, you reinitialize the TLS value to 0 (i.e. the value of account.Balance that you've passed to the Task<int> constructor). So no thread involved ever sees a value other than 0 during the context of executing any given task. A local variable would accomplish exactly the same thing, without the overhead of TLS and without confusing anyone who reads the code and tries to figure out why TLS was used when it adds no value to the code.
So, does TLS solve some sort of "data race problem"? Not in this example, it doesn't appear to. So asking how it does that is impossible to answer. It doesn't do that, so there is no "how".
For what it's worth, I modified your example slightly so that it would report the individual threads that were assigned to the tasks. I found that on my machine, the number of threads used varied between two and eight. This is consistent with my eight-core machine, with the variation due to how much the first thread in the pool can get done before the pool has initialized additional threads and assigned tasks to them. Most commonly, I would see the first thread completing between three and five of the tasks, with the remaining tasks handled by remaining individual threads.
In each case, the thread pool created eight threads as soon as the tasks were started. But most of the time, at least one of those threads wound up unused, because the other threads were able to complete the tasks before the pool was saturated. That is, there is overhead in the thread pool just managing the tasks, and in your example the tasks are so inexpensive that this overhead allows one or more thread pool threads to finish one task before the thread pool needs that thread for another.
I've copied that version below. Note that I also added a delay between trial iterations, to allow the thread pool to terminate the threads it created (on my machine, this took 20 seconds, hence the delay time hard-coded…you can see the threads being terminated in the debugger output).
static void Main(string[] args)
{
while (_PromptContinue())
{
// create the bank account instance
BankAccount account = new BankAccount();
// create an array of tasks
Task<int>[] tasks = new Task<int>[10];
// create the thread local storage
ThreadLocal<int> tlsBalance = new ThreadLocal<int>();
ThreadLocal<(int Id, int Count)> tlsIds = new ThreadLocal<(int, int)>(
() => (Thread.CurrentThread.ManagedThreadId, 0), true);
for (int i = 0; i < 10; i++)
{
int k = i;
// create a new task
tasks[i] = new Task<int>((stateObject) =>
{
// get the state object and use it
// to set the TLS data
tlsBalance.Value = (int)stateObject;
(int id, int count) = tlsIds.Value;
tlsIds.Value = (id, count + 1);
Console.WriteLine($"task {k}: thread {id}, initial value {tlsBalance.Value}");
// enter a loop for 1000 balance updates
for (int j = 0; j < 1000; j++)
{
// update the TLS balance
tlsBalance.Value++;
}
// return the updated balance
return tlsBalance.Value;
}, account.Balance);
// start the new task
tasks[i].Start();
}
// Make sure this thread isn't busy at all while the thread pool threads are working
Task.WaitAll(tasks);
// get the result from each task and add it to
// the balance
for (int i = 0; i < 10; i++)
{
account.Balance += tasks[i].Result;
}
// write out the counter value
Console.WriteLine("Expected value {0}, Balance: {1}", 10000, account.Balance);
Console.WriteLine("{0} thread ids used: {1}",
tlsIds.Values.Count,
string.Join(", ", tlsIds.Values.Select(t => $"{t.Id} ({t.Count})")));
System.Diagnostics.Debug.WriteLine("done!");
_Countdown(TimeSpan.FromSeconds(20));
}
}
private static void _Countdown(TimeSpan delay)
{
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
TimeSpan remaining = delay - sw.Elapsed,
sleepMax = TimeSpan.FromMilliseconds(250);
int cchMax = $"{delay.TotalSeconds,2:0}".Length;
string format = $"\r{{0,{cchMax}:0}}", previousText = null;
while (remaining > TimeSpan.Zero)
{
string nextText = string.Format(format, remaining.TotalSeconds);
if (previousText != nextText)
{
Console.Write(format, remaining.TotalSeconds);
previousText = nextText;
}
Thread.Sleep(remaining > sleepMax ? sleepMax : remaining);
remaining = delay - sw.Elapsed;
}
Console.Write(new string(' ', cchMax));
Console.Write('\r');
}
private static bool _PromptContinue()
{
Console.Write("Press Esc to exit, any other key to proceed: ");
try
{
return Console.ReadKey(true).Key != ConsoleKey.Escape;
}
finally
{
Console.WriteLine();
}
}
In this example, is this the correct use of the Parallel.For loop if I want to limit the number of threads that can perform the function DoWork to ten at a time? Will other threads be blocked until one of the ten threads becomes available? If not, what is a better multi-threaded solution that would still let me execute that function 6000+ times?
class Program
{
static void Main(string[] args)
{
ThreadExample ex = new ThreadExample();
}
}
public class ThreadExample
{
int limit = 6411;
public ThreadExample()
{
Console.WriteLine("Starting threads...");
int temp = 0;
Parallel.For(temp, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i =>
{
DoWork(temp);
temp++;
});
}
public void DoWork(int info)
{
//Thread.Sleep(50); //doing some work here.
int num = info * 5;
Console.WriteLine("Thread: {0} Result: {1}", info.ToString(), num.ToString());
}
}
You need to use the i passed to the lambda function as index. Parallel.For relieves you from the hassle of working with the loop counter, but you need to use it!
Parallel.For(0, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i =>
{
DoWork(i);
});
As for your other questions:
Yes, this will correctly limit the amount of threads working simultaneously.
There are no threads being blocked. The iterations are queued and as soon as a thread becomes available, it takes the next iteration (in a synchronized manner) from the queue to process.
I have an integration service which runs a calculation heavy, data bound process. I want to make sure that there are never more than say, n = 5, (but n will be configurable, changeable at runtime) of these processes running at the same. The idea is to throttle the load on the server to a safe level. The amount of data processed by the method is limited by batching, so I don't need to worry about 1 process representing a much bigger load than another.
The processing method is called by another process, where requests to run payroll are held on a queue, and I can insert some logic at that point to determine whether to process this request now, or leave it on the queue.
So i want a seperate method on the same service as the processing method, which can tell me if the server can accept another call to the processing method. It's going to ask, "how many payroll runs are going on? is that less than n?" What's a neat way of achieving this?
-----------edit------------
I think I need to make it clear, the process that decides whether to take the request off the queue this is seperated from the service that processes the payroll data by a WCF boundary. Stopping a thread on the payroll processing process isn't going to prevent more requests coming in
You can use a Semaphore to do this.
public class Foo
{
private Semaphore semaphore;
public Foo(int numConcurrentCalls)
{
semaphore = new Semaphore(numConcurrentCalls, numConcurrentCalls);
}
public bool isReady()
{
return semaphore.WaitOne(0);
}
public void Bar()
{
try
{
semaphore.WaitOne();//it will only get past this line if there are less than
//"numConcurrentCalls" threads in this method currently.
//do stuff
}
finally
{
semaphore.Release();
}
}
}
Review the Object Pool pattern. This is what you're describing. While not strictly required by the pattern, you can expose the number of objects currently in the pool, the maximum (configured) number, the high-watermark, etc.
I think that you might want a BlockingCollection, where each item in the collection represents one of the concurrent calls.
Also see IProducerConsumerCollection.
If you were just using threads, I'd suggest you look at the methods for limiting thread concurrency (e.g. the TaskScheduler.MaximumConcurrencyLevel property, and this example.).
Also see ParallelEnumerable.WithDegreeOfParallelism
void ThreadTest()
{
ConcurrentQueue<int> q = new ConcurrentQueue<int>();
int MaxCount = 5;
Random r = new Random();
for (int i = 0; i <= 10000; i++)
{
q.Enqueue(r.Next(100000, 200000));
}
ThreadStart proc = null;
proc = () =>
{
int read = 0;
if (q.TryDequeue(out read))
{
Console.WriteLine(String.Format("[{1:HH:mm:ss}.{1:fff}] starting: {0}... #Thread {2}", read, DateTime.Now, Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(r.Next(100, 1000));
Console.WriteLine(String.Format("[{1:HH:mm:ss}.{1:fff}] {0} ended! #Thread {2}", read, DateTime.Now, Thread.CurrentThread.ManagedThreadId));
proc();
}
};
for (int i = 0; i <= MaxCount; i++)
{
new Thread(proc).Start();
}
}
I have an app that takes on unknown amount of task. The task are blocking (they wait on network) i'll need multiple threads to keep busy.
Is there an easy way for me to have a giant list of task and worker threads which will pull the task when they are idle? ATM i just start a new thread for each task, which is fine but i'd like some control so if there are 100task i dont have 100threads.
Assuming that the network I/O classes that you are dealing with expose Begin/End style async methods, then what you want to do is use the TPL TaskFactory.FromAsync method. As laid out in TPL TaskFactory.FromAsync vs Tasks with blocking methods, the FromAsync method will use async I/O under the covers, rather than keeping a thread busy just waiting for the I/O to complete (which is actually not what you want).
The way that Async I/O works is that you have a pool of threads that can handle the result of I/O when the result is ready, so that if you have 100 outstanding I/Os you don't have 100 threads blocked waiting for those I/Os. When the whole pool is busy handling I/O results, subsequent results get queued up automatically until a thread frees up to handle them. Keeping a huge pool of threads waiting like that is a scalability disaster- threads are hugely expensive objects to keep around idling.
here a msdn sample to manage through a threadpool many threads:
using System;
using System.Threading;
public class Fibonacci
{
public Fibonacci(int n, ManualResetEvent doneEvent)
{
_n = n;
_doneEvent = doneEvent;
}
// Wrapper method for use with thread pool.
public void ThreadPoolCallback(Object threadContext)
{
int threadIndex = (int)threadContext;
Console.WriteLine("thread {0} started...", threadIndex);
_fibOfN = Calculate(_n);
Console.WriteLine("thread {0} result calculated...", threadIndex);
_doneEvent.Set();
}
// Recursive method that calculates the Nth Fibonacci number.
public int Calculate(int n)
{
if (n <= 1)
{
return n;
}
return Calculate(n - 1) + Calculate(n - 2);
}
public int N { get { return _n; } }
private int _n;
public int FibOfN { get { return _fibOfN; } }
private int _fibOfN;
private ManualResetEvent _doneEvent;
}
public class ThreadPoolExample
{
static void Main()
{
const int FibonacciCalculations = 10;
// One event is used for each Fibonacci object
ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
Random r = new Random();
// Configure and launch threads using ThreadPool:
Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
for (int i = 0; i < FibonacciCalculations; i++)
{
doneEvents[i] = new ManualResetEvent(false);
Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
fibArray[i] = f;
ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
}
// Wait for all threads in pool to calculation...
WaitHandle.WaitAll(doneEvents);
Console.WriteLine("All calculations are complete.");
// Display the results...
for (int i= 0; i<FibonacciCalculations; i++)
{
Fibonacci f = fibArray[i];
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
}
}
}