W
W
wkololo_4ever2014-03-03 14:18:56
ASP.NET
wkololo_4ever, 2014-03-03 14:18:56

How to properly organize asp.net multithreading?

There is a code, a function that changes the resolution of the image.

public void NewImage(int needHeight, int needWidth, string oldFile, string newFile)
        {
            double newWidth, newHeight;
            double UVR;
            Image FullSizeImage = Image.FromFile(oldFile);
            FullSizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
            FullSizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);

            if (FullSizeImage.Width > FullSizeImage.Height)
            {
                UVR = FullSizeImage.Width / needWidth;
                newWidth = needWidth;
                newHeight = FullSizeImage.Height / UVR;
            }
            else
            {
                if (FullSizeImage.Height > FullSizeImage.Width)
                {
                    UVR = FullSizeImage.Height / needHeight;
                    newHeight = needHeight;
                    newWidth = FullSizeImage.Width / UVR;
                }
                else
                {
                    newHeight = needHeight;
                    newWidth = needWidth;
                }
            }

            Image NewImage = FullSizeImage.GetThumbnailImage(Convert.ToInt32(newWidth), Convert.ToInt32(newHeight), null, IntPtr.Zero);
            FullSizeImage.Dispose();
            NewImage.Save(newFile);

        }

There is a situation, if the picture is very large, then the execution will not be instantaneous. I'm wondering: will the entire server hang during the execution of this code?
That is, do I need to write the selection of the second thread myself?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey Odinokov, 2014-03-30
@wkololo_4ever

For image resizing, you can use a ready-made solution - imageresizing.net. It is implemented as an HTTP Module for ASP.NET and performs image resizing on the fly. That is, during the loading of the picture, only its loading is performed, without transformations. Resizing is performed when a specific size of the image itself is requested and is performed asynchronously, since the page is already displayed to the user. A cache is also used to avoid constant regeneration of pictures.
But since the question was about "asp.net multithreading", I will also share my thoughts on this topic. I must say right away that there is a ready-made solution - HangFire , but the reasons for its appearance are also interesting.
In ASP.NET, each request is executed within a separate thread. Since the creation of a thread is a rather "expensive" operation, for each request a ready-made thread is "leased" from the thread pool.
There are a lot of rumors on the Internet that long-running ASP.NET requests can "clog" all threads from the pool, and new requests will mercilessly enter the request queue and wait for their turn to be executed. This situation is called Thread pool starvation (or thread pool contention) and leads to a decrease in server throughput as the number of requests increases.
However, since IIS 7, the number of available threads in an ASP.NET application is quite large .. But since image resizing is a cpu-intensive task, a decrease in throughput can still occur due to a lack of processor resources, which is exacerbated by expensive and constant switching between threads.
ASP.NET in general and ASP.NET MVC in particular have the concept of asynchronous request processing. MVC 3 had an AsyncController, starting with MVC 4 and C# 5.0, support for async methods was introduced . However, in this programming model, the request mustwait for all asynchronous operations to complete, and in your case it will turn out that you give the image resize to be executed in another thread, and wait for it to complete in the thread that executes the request. In this case, you do not win anything, and even lose, because instead of one thread, two threads begin to be allocated to process the request.
In an ASP.NET application, you can also either create a thread yourself (which is bad because it is an expensive operation), or use some pool of pre-initiated threads yourself, or use the same Thread Pool (via the Task-based API or ThreadPool.QueueUserWorkItem) and not wait for its completion, while the image resize will occur outside the request processing context. In this case, the user will not have to wait for the long operation to complete, but the following problem may arise here.
If IIS is the web server (which probably was until Owin went mainstream), then your application is hosted in one or more IIS worker processes that run under the application pool. And the application pool has many different settings that can lead to application recycling . This is a good process that saves server resources, especially on shared hosting. But it has a timeout for its completion, which is 90 seconds by default.
When the application pool starts the recycling process, it sends a shutdown command to the ASP.NET application. At the same time, the ASP.NET application itself stops accepting new requests, and waits for the current ones to complete with its own default timeout (ShutdownTimeout in the application pool config) of 30 seconds. If the current requests manage to be completed within the allotted time, then immediately after that the application domain is unloaded.
This is where the first problem lies - ASP.NET, by default, simply does not pay attention to all your background threads, both those created by yourself and threads from the Thread Pool. And if, after completing the processing of current requests, your background task was not completed - these are your problems, it will simply be interrupted by a ThreadAbortException. And if specific steps were not taken to restart it, then later you yourself will explain to users why sometimes pictures or reports do not reach them. Of course, the probability seems to be small, but there are many requests, there are many reasons for recycling, and the lifetime of the project is also large. And dealing with the consequences is quite unpleasant.
ASP.NET also has the IRegisteredObject interface and the HostEnvironment.RegisterObject and HosterEnvironment.UnregisterObject methods (and here areSee them ) that notify ASP.NET that there is an operation to wait for to complete when the application stops event is received. True, ShutdownTimeout does not go anywhere, and it is very difficult to guarantee that all background tasks will be completed in 30 seconds.
So the main problem is the possibility of interrupting the thread executing the task due to timeouts. I have personally caught such exceptions a few times, and resolving the consequences was sometimes quite a nuisance. Increasing the timeouts is not entirely reasonable, since they help the application maintain at least some stability when there are much bigger problems for completely different reasons.
In this situation, a 100% working solution is to write your own application that will "spin" constantly and expose it as a Windows service. It will restart much less frequently, and timeouts can be adjusted to your heart's content. However, in addition, you still need to organize a communication protocol (here you can take, of course, WCF), and develop a competent task processing model. But both initial development and subsequent testing are somewhat difficult. And to justify such a monstrous decision by the fact that "we need a guarantee when resizing images" can also be quite difficult.
The most annoying thing for me was that Ruby on Rails was full of all sorts of solutions that solved a similar problem with long-running processes on the web server side in a general manner: Resque , Sidekiq ,delayed_job and others like them. For the .NET platform, there are several developments on NuGet, but they are still too weak, and sometimes do not guarantee that the task will always be executed after it is created.
After waiting a year or two, I decided to make a similar general tool for .NET - HangFire . At the moment, it has version 0.7, which means it is not fully ready for mass use, but it is much more stable and functional than most of its own developments. Plus, it's currently under active development. The schema and public API are fairly stable and are already in use on our production system. So you can try it, and if you have any questions, then feel free to create a ticket on the github.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question