Кэшируйте данные на диске веб-сервера. Кэшируйте часто используемые данные на сервере

Sponsored By

A few years back Phil Haack wrote a great article on the dangers of recurring background tasks in ASP.NET . In it he points out a few gotchas that are SO common when folks try to do work in the background. Read it, but here"s a summary from his post.

  • An unhandled exception in a thread not associated with a request will take down the process.
  • If you run your site in a Web Farm, you could end up with multiple instances of your app that all attempt to run the same task at the same time.
  • The AppDomain your site runs in can go down for a number of reasons and take down your background task with it.

If you think you can just write a background task yourself, it"s likely you"ll get it wrong. I"m not impugning your skills, I"m just saying it"s subtle . Plus, why should you have to?

There"s LOT of great ways for you to do things in the background and a lot of libraries and choices available.

Some ASP.NET apps will be hosted in IIS in your data center and others will be hosted in the Azure cloud. The spectrum of usage is roughly this, in my opinion:

  • General: Hangfire (or similar similar open source libraries)
    • used for writing background tasks in your ASP.NET website
  • Cloud:
    • A formal Azure feature used for offloading running of background tasks outside of your Website and scale the workload
  • Advanced: Azure Worker Role in a Cloud Service
    • scale the background processing workload independently of your Website and you need control over the machine

There"s lots of great articles and videos on how to use , and lots of documentation on how Worker Roles in scalable Azure Cloud Services work, but not a lot about how your hosted ASP.NET application and easily have a background service. Here"s a few.

WebBackgrounder

As it says "WebBackgrounder is a proof-of-concept of a web-farm friendly background task manager meant to just work with a vanilla ASP.NET web application." Its code hasn"t been touched in years, BUT the WebBackgrounder NuGet package has been downloaded almost a half-million times.

The goal of this project is to handle one task only, manage a recurring task on an interval in the background for a web app.

If your ASP.NET application just needs one background task to runs an a basic scheduled interval, than perhaps you just need the basics of WebBackgrounder.

Using System;
using System.Threading;
using System.Threading.Tasks;

namespace WebBackgrounder.DemoWeb
{
public class SampleJob: Job
{
public SampleJob(TimeSpan interval, TimeSpan timeout)
: base("Sample Job", interval, timeout)
{
}

Public override Task Execute()
{
return new Task(() => Thread.Sleep(3000));
}
}
}

Built in: QueueBackgroundWorkItem - Added in .NET 4.5.2

Somewhat in response to the need for WebBackgrounder, .NET 4.5.2 added QueueBackgroundWorkItem as a new API . It"s not just a "Task.Run," it tries to be more:

QBWI schedules a task which can run in the background, independent of any request. This differs from a normal ThreadPool work item in that ASP.NET automatically keeps track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.

It can try to delay an AppDomain for as long as 90 seconds in order to allow your task to complete. If you can"t finish in 90 seconds, then you"ll need a different (and more robust, meaning, out of process) technique.

The API is pretty straightforward, taking Func. Here"s an example that kicks of a background work item from an MVC action:

Public ActionResult SendEmail( User user)
{
if (ModelState.IsValid)
{
HostingEnvironment.QueueBackgroundWorkItem(ct => SendMailAsync(user.Email));
return RedirectToAction("Index", "Home");
}

Return View(user);
}

FluentScheduler

FluentScheduler is a more sophisticated and complex scheduler that features a (you guessed it) fluent interface. You have really explicit control over when your tasks run.

Using FluentScheduler;

public class MyRegistry: Registry
{
public MyRegistry()
{
// Schedule an ITask to run at an interval
Schedule().ToRunNow().AndEvery(2).Seconds();

// Schedule a simple task to run at a specific time
Schedule(() => Console.WriteLine("Timed Task - Will run every day at 9:15pm: " + DateTime.Now)).ToRunEvery(1).Days().At(21, 15);

// Schedule a more complex action to run immediately and on an monthly interval
Schedule(() =>
{
Console.WriteLine("Complex Action Task Starts: " + DateTime.Now);
Thread.Sleep(1000);
Console.WriteLine("Complex Action Task Ends: " + DateTime.Now);
}).ToRunNow().AndEvery(1).Months().OnTheFirst(DayOfWeek.Monday).At(3, 0);
}
}

FluentScheduler also embraces IoC and can easily plug into your favorite Dependency Injection tool of choice by just implementing their ITaskFactory interface.

Quartz.NET

Quartz.NET is a .NET port of the popular Java job scheduling framework of the (almost) same name. It"s very actively developed. Quartz has an IJob interface with just one method, Execute, to implement.

Using Quartz;
using Quartz.Impl;
using System;

namespace ScheduledTaskExample.ScheduledTasks
{
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();

IJobDetail job = JobBuilder.Create().Build();

ITrigger trigger = TriggerBuilder.Create()
.WithDailyTimeIntervalSchedule
(s =>
s.WithIntervalInHours(24)
.OnEveryDay()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0))
.Build();

Scheduler.ScheduleJob(job, trigger);
}
}
}

Then, inside your Application_Start, you call JobScheduler.Start(). There"s a great getting started article on Quartz at Mikesdotnetting you should check out.

Hangfire

And last but definitely not least, the most polished (IMHO) of the group,

Я хочу, чтобы мой сайт мог размещаться в облаке в будущем, а также чтобы он мог обрабатывать множество запросов.

Насколько безопасны статические переменные?

Являются ли они небезопасными, потому что отдельные запросы отдельных пользователей фактически используют эти статические переменные? Или это потому, что если вы распространяете сайт поверх потоков/осколков или подобных, (для обработки больших нагрузок) потоки разделяют статические переменные?

В основном у меня есть классы-помощники со статическими свойствами, следует ли мне изменить эту архитектуру, чтобы вместо этого создать экземпляр каждого класса и получить доступ к экземплярам?

E.G Вот пример того, что я делаю:

Using System; using System.Collections.Generic; using System.Linq; using System.Web; using Mvc.Mailer; namespace MVCWebsite.Helpers { public class AppSettings { public static void OnAppInit() { //General AppName = "MyApp"; DesktopBaseURLs = new Dictionary(); DesktopBaseURLs.Add("dev", "localhost:50560"); DesktopBaseURLs.Add("test", "www.test.whatever.com"); DesktopBaseURLs.Add("live", "www.whatever.com"); MobileBaseURLs = new Dictionary(); MobileBaseURLs.Add("dev", "m.local.whatever.com"); MobileBaseURLs.Add("test", "m.test.whatever.com"); MobileBaseURLs.Add("live", "m.whatever.com"); //Emails EmailHostName = AppName + ".com"; //For the moment atleast NoReplyEmailAddress = "no-reply@" + EmailHostName.ToLower(); SupportEmailAddress = "support@" + EmailHostName.ToLower(); ErrorEmailAddress = "errors@" + EmailHostName.ToLower(); //Resources TempFileURL = "/content/temp/"; UserDataURL = "/content/user-content/"; ProfilePicturesURL = UserDataURL + "profile-pictures/"; var a = GlobalHelper.GetURLAsServerPath(ProfilePicturesURL); var b = a; } //General public static string AppName { get; set; } public static Dictionary DesktopBaseURLs; public static Dictionary MobileBaseURLs; //Emails public static string EmailHostName { get; set; } public static string NoReplyEmailAddress { get; set; } public static string SupportEmailAddress { get; set; } public static string ErrorEmailAddress { get; set; } //Resources public static string UserDataURL { get; set; } public static string TempFileURL { get; set; } public static string ProfilePicturesURL { get; set; } //Methods public static void SetAppURL() { } } }

2 ответов

Ваш код не является потокобезопасным. Вы используете статические переменные между несколькими запросами, которые потенциально могут выполняться несколькими потоками. Имейте в виду, что класс Dictionary , который вы используете в качестве основного хранилища, не является потокобезопасным классом, означающим, что ваш код может сильно сильно пострадать, если вы попытаетесь вызвать метод OnAppInit одновременно из нескольких потоков. Если, с другой стороны, вы вызываете этот статический метод OnAppInit только один раз внутри вашего события Application_Start (который, как гарантируется, будет выполняться только один раз из одного потока), вы вполне можете его использовать.

Говорят, что статические переменные и методы, как правило, являются плохими идеями в приложениях, это неверно. Это плохая идея, если вы не знаете, как правильно их использовать, или если вы не знаете, как синхронизировать доступ к ним, если вам нужно, чтобы это выполнялось из параллельных потоков. Написание потокобезопасного кода - очень сложный вопрос, и если у вас есть опасения ошибиться (кто не пишет многопоточные приложения, такие как приложение ASP.NET?) Просто не разделяет это состояние. Используйте хорошо установленные места для этого в приложении ASP.NET:

  • Backend (может быть, например, реляционная база данных)
  • Состояние приложения
  • Контекстное состояние Http
  • Состояние сеанса
  • Файлы cookie клиента

Эти места специально предназначены для хранения состояния в приложении ASP.NET(кроме первого, конечно, который может быть использован в любом типе приложения).

Для ресайзинга картинок вы можете использовать готовое решение – imageresizing.net. Оно выполнено в виде HTTP Module для ASP.NET и производит ресайзинг картинок "на лету". То есть во время загрузки картинки производится только ее загрузка, без трансформаций. Ресайзинг же производится тогда, когда запрашивается специфичный размер самой картинки и выполняется асинхронно, поскольку страница уже отображается у пользователя. Также используется кэш, чтобы избежать постоянной регенерации картинок.

Но так как вопрос был про "многопоточность asp.net", то поделюсь соображениями еще и по этой теме. Сразу скажу, что есть готовое решение – HangFire , но также интересны и причины его появления.

В ASP.NET каждый запрос выполняется внутри отдельного потока. Так как создание потока является достаточно "дорогой" операцией, то для каждого запроса отдается "в аренду" готовый поток из пула потоков.

В интернете ходит множество слухов, что долгоиграющие запросы ASP.NET могут "забить" все потоки из пула, и новые запросы будут нещадно попадать в request queue и ожидать своей очереди на исполнение. Такая ситуация называется Thread pool starvation (или thread pool contention) и приводит к снижению пропускной способности сервера при увеличении количества запросов.

Однако начиная с IIS 7 количество доступных потоков в ASP.NET приложении достаточно большое . Но так как ресайзинг картинки является cpu intensive задачей, то снижение пропускной способности все-таки может произойти из-за нехватки ресурсов процессора, которая усугубляется дорогими и постоянными переключениями между потоками.

В ASP.NET в общем и в ASP.NET MVC в частности есть понятие асинхронной обработки запросов. В MVC 3 был AsyncController, начиная с MVC 4 и C# 5.0 была введена поддержка async-методов . Однако в этой программной модели запрос должен ждать завершения всех асинхронных операций, и в вашем случае получится, что вы отдаете ресайз картинки на выполнение в другом потоке, и ожидаете его завершение в потоке, который исполняет запрос. В этом случае вы ничего не выигрываете, и даже проигрываете, потому что вместо одного потока на обработку запроса начинает выделяться два потока.

В ASP.NET приложении вы также можете либо создать поток самостоятельно (что плохо, потому что это дорогая операция), либо использовать некоторый пул предварительно инициированных потоков самостоятельно, либо использовать тот же самый Thread Pool (посредством Task-based API либо ThreadPool.QueueUserWorkItem) и не ожидать его завершения, при этом ресайз картинки будет происходить вне пределов контекста обработки запроса. В этом случае пользователю не нужно будет ожидать завершения долгой операции, но здесь может возникнуть следующая проблема.

Если в качестве веб-сервера выступает IIS (что скорее всего так, пока Owin не пошел в массы), то ваше приложение хостится в одном или нескольких рабочих процессах IIS, которые работают под управлением пула приложения. А пул приложения имеет множество всевозможных настроек, которые могут привести к recycling"у приложения . Это хороший процесс, который экономит ресурсы сервера, особенно на shared хостингах. Вот только у него есть таймаут на его завершение, который по-умолчанию равен 90 секундам.

Когда application pool начинает процесс ресайклинга, он отсылает ASP.NET приложению команду на остановку (shutdown). При этом само ASP.NET приложение перестает принимать новые запросы, и ждет завершения текущих с собственным дефолтным таймаутом (ShutdownTimeout в конфиге application pool) в 30 секунд. Если текущие запросы успевают выполниться за отведенное время, то сразу после этого происходит выгрузка домена приложения.

Вот тут и кроется первая проблема – ASP.NET по-умолчанию просто не обращает внимания на все ваши фоновые потоки, как созданные собственноручно, так и потоки из Thread Pool. И если после завершения обработки текущих запросов ваша фоновая задача не была завершена – это ваши проблемы, она будет просто прервана исключением ThreadAbortException. И если не были предприняты конкретные шаги по ее перезапуску, то сами потом будете объяснять пользователям, почему иногда картинки или отчеты до них так и не доходят. Конечно же, вероятность вроде бы небольшая, но запросов много, причин у recycling"а много, да и время жизни проекта тоже большое. А бороться с последствиями довольно неприятно.

Еще в ASP.NET есть интерфейс IRegisteredObject и методы HostEnvironment.RegisterObject и HosterEnvironment.UnregisterObject (а вот и ), которые уведомляют ASP.NET о том, что есть операция, завершения которой нужно ожидать при получения события об остановке приложения. Правда вот ShutdownTimeout при этом никуда не девается, а гарантировать, что все фоновые задачи будут завершены за 30 секунд очень сложно.

Так что основная проблема – это вероятность прерывания потока, выполняющего задачу из-за таймаутов. Я лично ловил несколько раз такие исключения, и разрешение последствий иногда было довольно неприятной штукой. Увеличивать же таймауты не совсем разумно, поскольку именно они помогают приложению сохранять хоть какую-то стабильность, когда возникают гораздо большие проблемы по совершенно другим причинам.

В этой ситуации 100% работающее решение – написать собственное приложение, которое будет "крутиться" постоянно и оформить его в виде службы Windows. Она будет перезапускаться гораздо реже, и таймауты можно настроить так, как душе угодно. Однако в добавок нужно еще организовать протокол общения (тут можно взять, конечно WCF), и разработать грамотную модель обработки задач. Но и первоначальная разработка, и последующее тестирование несколько сложно. А оправдать такое монстроидальное решение тем, что "нам нужна гарантия при ресайзе картинок" бывает тоже довольно трудно.

Самым обидным для меня было то, что для Ruby on Rails было полно всяких решения, которые решали подобную проблему с long-running процессами на стороне веб-сервера на общий манер: Resque , Sidekiq , delayed_job и иже с ними. Для платформы.NET есть несколько разработок на NuGet, но они пока еще слишком слабые, и иногда не дают гарантий того, что задача будет выполнена всегда после ее создания.

Подождав год-другой, я решил все-таки сделать подобный общий инструмент для.NET – HangFire . На данный момент у него версия 0.7, что означает его неполную готовность для массового использования, однако он гораздо стабильнее и функциональнее, чем большинство собственных разработок. Плюс, он сейчас находится в активной разработке. Схема и публичное API довольно стабильно, и оно уже используется в нашей системе, которая находится в продакшене. Так что можете попробовать, а если возникнут вопросы – то смело создавайте тикет на гитхабе.

This tutorial attempts to shed some light on the subject of Threading using ASP.NET. Threading is a technique used to give the user the impression that multiple tasks are executing at the same time. The .NET Framework provides us with many types that allow us to easily and quickly add multi-threading to our .NET Web application. I’ll assume that you have some familiarity with ASP.NET and at least some experience with writing code in Visual Basic.

Through the tutorial we’ll build a pretty simple Web application that should be a good example of some basic threading concepts. Basically we’ll have 2 textboxes in which to enter a phrase, and to enter a time period in which you want the phrase to be rebuilt. Our Web threading application consists of 2 pages, one of which is the page that will start the thread, while the other is a results page that shows us the progress of our thread. The code provided should be very portable and allow you to implement your own threading application very quickly.

Before we get started…

Before we dive into the Web application, let me first give you a quick look at some of the code that you’ll be seeing.

First, we need to import the System.Threading Namespace so we can access the Thread class provided in the .NET Framework. Add this line to the top of your .aspx/.ascx file:

<%@ Import NameSpace="System.Threading" %>

Imports System.Threading

Now for demonstration purposes, here is a sample long running method. In a real life situation this method would most likely perform a task like processing a Web form, or completing a time-consuming database query.

Public Sub SomeLongMethod()

"your code that takes a while to execute

Now to execute this method and not leave our Web form hanging, we’ll start a new thread and let SomeLongMethod execute on this new thread. To do this, we have a few options. The technique I’ll use is to set up a new method that will start our new thread running. Here’s a sample thread starter function:

Public Sub SomeLongMethod_Thread()

"first, declare a new Thread, passing the constructor the address
"of SomeLongMethod. NOTE: SomeLongMethod can be replaced with your
"own method

Dim NewThread As Thread = New _
Thread(AddressOf SomeLongMethod)

"next we set the priority of our thread to lowest, setting
"the priority to a higher level can cause unexpected results.
NewThread.Priority = ThreadPriority.Lowest

"finally we start the thread executing
NewThread.Start()

And that’s it! All we have to do now is replace our call to SomeLongMethod with a call to SomeLongMethod_Thread , and the long method will execute on its own thread. Normally, we would redirect the user to the results page at the end of the SomeLongMethod_Thread method. However in this example I left that out to prevent confusion — I’ll demonstrate it in the following example, which illustrates the use of Threading in an ASP.NET Web application.

Using Threading to Rebuild a String

The first file we’ll look at is default.aspx. This will be the page where we’ll get 2 values from the user, and start a new thread. The second file we’ll look at is the results page, where we’ll poll the Session variables created in the thread, and display current thread stats to our user. I’ll go through default.aspx method-by-method, and then do the same for the results.aspx page. The source code for this file is available at the end of this tutorial.

NOTE: I’ve assumed that your Web server has Session variables enabled. If you have Session variables Disabled, or you have cookies disabled on your browser, the results of the following example will not display correctly.

A Simple Example (default.aspx)

Let’s begin by looking at the Page_Load function for default.aspx

Sub Page_Load(ByVal sender As System.Object, ByVal e
As System.EventArgs)

SyncLock Session.SyncRoot
"here, we initialize 3 session variables to hold our results
Session("Complete") = False
Session("Status") = ""
Session("Phrase") = ""
End SyncLock
d Sub

In this method we simply initialize 3 session variables that we’ll use in our next few methods. Session("Complete") will be the sentinel for our results page. When the thread is complete we will set Session("Complete") to True . The Session("Phrase") variable will be what we use to hold our partial phrase as we slowly build it. Session("Status") is just a variable to hold the start time and the end time. Now let’s look at our phrase re-building method:

Sub PhraseBuilder()

Dim str As String = ""
Dim i As Integer = 0

Dim startTimeTicks As Long = 0
Dim strStartTime As String = ""

Dim totalSleepTime As Double = 0.0

"log our start time, in ticks, and in Long Date format
startTimeTicks = DateTime.Now.Ticks
strStartTime = "Thread Started: " & DateTime.Now

" get phrase
str = txtPhrase.Text

"convert users time from seconds to milliseconds
totalSleepTime = 1000.0
totalSleepTime = totalSleepTime * CInt(txtTotalThreadLife.Text)
totalSleepTime = (totalSleepTime / str.Length)

For i = 0 To str.Length - 1

"this method will put our thread to sleep for the specified
"number of milliseconds. without the sleep, this method would
"execute too fast to see the thread working.
Thread.Sleep(totalSleepTime)

"we use synclock to block any other thread from accessing
"session variables while we are changing their values.
SyncLock Session.SyncRoot

Session("Status") = "Thread is " & _
Format((i / (str.Length - 1)) * 100, "#0.00") & _
"% complete." & " - Time Elapsed: " & _
Format((DateTime.Now.Ticks - startTimeTicks) / 10000000 _
, "#0.00") & " sec. Target: " & txtTotalThreadLife.Text & _
".00 sec."

SyncLock Session.SyncRoot
"rebuild phrase 1 letter at a time
Session("Phrase") &= str.Chars(i).ToString
End SyncLock

"our method is complete, so set the Session variables
"accordingly
SyncLock Session.SyncRoot
Session("Status") = strStartTime & _
"
Thread Complete. End Time: " & DateTime.Now & "
"

Session("Complete") = True
End SyncLock

Ok, now let’s dissect this method a little. Basically what we’re doing here is forcing a method that would otherwise run quickly to run on a schedule based on user input. This is done using the Thread.Sleep(ByVal millisecond as Integer) method. This method allows us to put the thread to sleep for the specified number of milliseconds. This Sleep method can be used in any method, not just one that’s executing on a new thread.

The other interesting technique we utilize is the use of the Synclock method. Synclock is used to block other threads from trying to obtain the same Synclock . To protect the variables from simultaneous access, we need to obtain the same Synclock before we access the variables everywhere else in the code. This is a necessity in a multi-threaded Web application to ensure that two methods aren’t reading/writing to the same variable at the same time. The Synclock method is identical to using the Monitor.Enter(Me) method that’s also provided in the System.Threading Namespace .

There are only two methods left to go! The next method we’ll look at is the PhraseBuilder_Thread function. This function is almost identical to the example at the beginning of the article:

Sub PhraseBuilder_Thread()

"method to start our phrase builder method executing
"on a new thread.

Dim myThread As Thread = New Thread(AddressOf PhraseBuilder)

MyThread.Priority = ThreadPriority.Lowest

"//start the new thread
myThread.Start()

"now redirect to the results page
Response.Redirect("results.aspx")

Now all that’s left is to call our PhraseBuilder_Thread method when the user clicks the submit button. Here’s the short code:

Sub btnSubmit_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs)

"start PhraseBuilder thread...
PhraseBuilder_Thread()
End Sub

A Simple Example(results.aspx)

Now we’ll take a look at the results page. Basically our results page will check the status of the Session("completed") variable on the Page_Load , and react accordingly. Here’s the Page_load function for results.aspx:

Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
"Put user code to initialize the page here
"check the value of Session("Completed"), if it is True, stop writing

If Session("Complete") <> True Then

"make sure session variables are enabled, if not warn user

If Session("Complete") <> False Then
"error with session variable, Session("Complete") is not
"True or False
lblComplete.Text = "Error with Session("Complete")"

"set page to auto refresh page every 2 seconds, until thread is done

Response.Write("")

SyncLock Session.SyncRoot
lblStatus.Text = Session("Status") & "
Processing. . ."

End SyncLock

Else
"thread is complete, stop writing refresh tag, and display
"results
SyncLock Session.SyncRoot
lblStatus.Text = Session("Status")
lblPhrase.Text = Session("Phrase")
lblComplete.Text = Session("Complete")
End SyncLock

This Page_Load function checks the status of the Session("Complete") variable and reacts accordingly. Feel free to customize you results page to suit you needs, but I recommend checking for the case when the Session("Completed") variable is not set to True, or False. This usually happens when Session variables are disabled, or you have cookies disabled. Also, you can remove the Response.Write statement if you don’t want the page to automatically refresh.

In Conclusion

Well that’s all there is to it! Hopefully you have just written your first ASP.NET application using threading.

A thread is defined as the execution path of a program. Each thread defines a unique flow of control. If your application involves complicated and time consuming operations such as database access or some intense I/O operations, then it is often helpful to set different execution paths or threads, with each thread performing a particular job.

Threads are lightweight processes. One common example of use of thread is implementation of concurrent programming by modern operating systems. Use of threads saves wastage of CPU cycle and increases efficiency of an application.

So far we compiled programs where a single thread runs as a single process which is the running instance of the application. However, this way the application can perform one job at a time. To make it execute multiple tasks at a time, it could be divided into smaller threads.

In .Net, the threading is handled through the "System.Threading" namespace. Creating a variable of the System.Threading.Thread type allows you to create a new thread to start working with. It allows you to create and access individual threads in a program.

Creating Thread

A thread is created by creating a Thread object, giving its constructor a ThreadStart reference.

ThreadStart childthreat = new ThreadStart(childthreadcall);

Thread Life Cycle

The life cycle of a thread starts when an object of the System.Threading.Thread class is created and ends when the thread is terminated or completes execution.

Following are the various states in the life cycle of a thread:

    The Unstarted State : It is the situation when the instance of the thread is created but the Start method is not called.

    The Ready State : It is the situation when the thread is ready to execute and waiting CPU cycle.

    The Not Runnable State : a thread is not runnable, when:

    • Sleep method has been called
    • Wait method has been called
    • Blocked by I/O operations
  • The Dead State : It is the situation when the thread has completed execution or has been aborted.

Thread Priority

The Priority property of the Thread class specifies the priority of one thread with respect to other. The .Net runtime selects the ready thread with the highest priority.

The priorities could be categorized as:

  • Above normal
  • Below normal
  • Highest
  • Lowest
  • Normal

Once a thread is created, its priority is set using the Priority property of the thread class.

NewThread.Priority = ThreadPriority.Highest;

Thread Properties & Methods

The Thread class has the following important properties:

Property Description
CurrentContext Gets the current context in which the thread is executing.
CurrentCulture Gets or sets the culture for the current thread.
CurrentPrinciple Gets or sets the thread"s current principal for role-based security.
CurrentThread Gets the currently running thread.
CurrentUICulture Gets or sets the current culture used by the Resource Manager to look up culture-specific resources at run time.
ExecutionContext Gets an ExecutionContext object that contains information about the various contexts of the current thread.
IsAlive Gets a value indicating the execution status of the current thread.
IsBackground Gets or sets a value indicating whether or not a thread is a background thread.
IsThreadPoolThread Gets a value indicating whether or not a thread belongs to the managed thread pool.
ManagedThreadId Gets a unique identifier for the current managed thread.
Name Gets or sets the name of the thread.
Priority Gets or sets a value indicating the scheduling priority of a thread.
ThreadState Gets a value containing the states of the current thread.

The Thread class has the following important methods:

Methods Description
Abort Raises a ThreadAbortException in the thread on which it is invoked, to begin the process of terminating the thread. Calling this method usually terminates the thread.
AllocateDataSlot Allocates an unnamed data slot on all the threads. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead.
AllocateNamedDataSlot Allocates a named data slot on all threads. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead.
BeginCriticalRegion Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception might endanger other tasks in the application domain.
BeginThreadAffinity Notifies a host that managed code is about to execute instructions that depend on the identity of the current physical operating system thread.
EndCriticalRegion Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception are limited to the current task.
EndThreadAffinity Notifies a host that managed code has finished executing instructions that depend on the identity of the current physical operating system thread.
FreeNamedDataSlot Eliminates the association between a name and a slot, for all threads in the process. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead.
GetData Retrieves the value from the specified slot on the current thread, within the current thread"s current domain. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead.
GetDomain Returns the current domain in which the current thread is running.
GetDomainID Returns a unique application domain identifier.
GetNamedDataSlot Looks up a named data slot. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead.
Interrupt Interrupts a thread that is in the WaitSleepJoin thread state.
Join Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping. This method has different overloaded forms.
MemoryBarrier Synchronizes memory access as follows: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier.
ResetAbort Cancels an Abort requested for the current thread.
SetData Sets the data in the specified slot on the currently running thread, for that thread"s current domain. For better performance, use fields marked with the ThreadStaticAttribute attribute instead.
Start Starts a thread.
Sleep Makes the thread pause for a period of time.
SpinWait Causes a thread to wait the number of times defined by the iterations parameter.
VolatileRead() Reads the value of a field. The value is the latest written by any processor in a computer, regardless of the number of processors or the state of processor cache. This method has different overloaded forms.
VolatileWrite() Writes a value to a field immediately, so that the value is visible to all processors in the computer. This method has different overloaded forms.
Yield Causes the calling thread to yield execution to another thread that is ready to run on the current processor. The operating system selects the thread to yield to.

Example

The following example illustrates the uses of the Thread class. The page has a label control for displaying messages from the child thread. The messages from the main program are directly displayed using the Response.Write() method. Hence they appear on the top of the page.

The source file is as follows:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="threaddemo._Default" %> Untitled Page

Thread Example

The code behind file is as follows:

Using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; using System.Threading; namespace threaddemo { public partial class _Default: System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { ThreadStart childthreat = new ThreadStart(childthreadcall); Response.Write("Child Thread Started
"); Thread child = new Thread(childthreat); child.Start(); Response.Write("Main sleeping for 2 seconds.......
"); Thread.Sleep(2000); Response.Write("
Main aborting child thread
"); child.Abort(); } public void childthreadcall() { try{ lblmessage.Text = "
Child thread started
"; lblmessage.Text += "Child Thread: Coiunting to 10"; for(int i =0; i<10; i++) { Thread.Sleep(500); lblmessage.Text += "
in Child thread
"; } lblmessage.Text += "
child thread finished"; }catch(ThreadAbortException e){ lblmessage.Text += "
child thread - exception"; }finally{ lblmessage.Text += "
child thread - unable to catch the exception"; } } } }

Observe the following

    When the page is loaded, a new thread is started with the reference of the method childthreadcall(). The main thread activities are displayed directly on the web page.

    The second thread runs and sends messages to the label control.

    The main thread sleeps for 2000 ms, during which the child thread executes.

    The child thread runs till it is aborted by the main thread. It raises the ThreadAbortException and is terminated.

    Control returns to the main thread.

When executed the program sends the following messages.