I have a Windows Forms application which uses around 200 threads and 160 of them are Forms threads.
I am gathering data into application with TCP connection, I enqueue the messages into many ConcurrentQueue<T>
s with a single thread. 40 other threads lets call them readerThreads are checking if there is any item in their queue and dequeue the message, and process into forms. Form threads processes their own queues and show the data.
One readerThread(threadA) in particular is processing the 90% of the messages from his concurrentQueue(queueA)
. So when live data starts to accumulate so fast, queueA
is swelled up and Application is starting to lag.
So I want to give queueA
a higher priority than others, and also I think I am losing so much time while OS changes between the threads so I want to let threadA run with everything OS can offer while queueA is getting dequeued. Currently I am using new Thread();
for every thread and not using any thread pool. I know it is not wise and want to change it to something better.
Firstly, can using ThreadPriority.Highest
on threadA
help me at this issue and secondly, what can you offer instead of using new Thread();
. Any example would be very helpful.
CodePudding user response:
160 of them are Forms threads
Assuming you mean UI threads, that just sounds insane. Normally there should be only one UI thread. There might be some special cases where more are warranted, but 160? Do you have 160 windows that display data?
I would assume your readers work something like
- Fetch data from queue
- Process data
- Update UI
If this is not keeping up you need to find out why, not just try to hide the problem by playing around with more threads. Start with a profiler, if the processing is slow, see if it can be optimized, or move it to one or more background threads. If the UI updates take to much time, see if you can reduce the frequency of UI updates, or reduce the complexity of the UI, possibly using custom rendering to get better control of what is rendered.
While using "new Thread()" most likely do not help, there is no telling if it is the actual problem. So you should always start by measuring, not changing things blindly in the hope that it helps.
CodePudding user response:
Windows and Wndprocs
A little background on how win32 works underneath the C# WinForms veneer. When a process starts, it's main thread registers a window with a window procedure or wndproc, tell Windows to create the window, and then goes into a loop waiting for Windows to give it messages to dispatch to its wndprocs.
This can be seen in the C code below:
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
https://docs.microsoft.com/en-us/windows/win32/learnwin32/your-first-windows-program
Creating 160 windows is simply a matter of registering at most 160 window classes and calling CreateWindowEx
160 times. You only need one thread to handle pumping the messages from the OS and dispatching them to the different wndprocs.
Using C# doesn't change the fact that this is still happening under the hood. You still only need 1 thread to manage the messages from the OS. Having a thread per window will only cause context thrashing.
Updating 160 windows
Let's say the OS wants to send 1 message to each window.
160 threads for 160 windows
In this case the OS:
- Puts 1 message in queue for 1 thread
- Switches context and control over to the 1 thread
- Said thread gets the message, dispatches it to the wndproc
- Wndproc handles the message
- Thread asks for the next message and is put to sleep
- OS repeats steps 159 more times
Each time the OS and the threads transfer control back and forth, there is a cost incurred as the OS saves the state of the thread, schedules the next thread, and loads the state of the next thread.
1 thread for 160 windows
Consider the alternative:
- The os puts 160 messages in queue for the main thread
- Switches context and control over to the main thread
- Main thread gets the message, and dispatches it to the proper wndproc
- Wndproc handles the message
- Thread repeats 159 times
- Thread sleeps
In this case, context switching is minimized.