分享
 
 
 

dotNet Threading, Part II

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

dotNet Threading, Part IIAuthor: Randy Morin

Date Added: 17th Mar 2003

Type: Tutorial

Rating:

This is a printable version of "dotNet Threading, Part II". For the complete online version, please visit http://www.devarticles.com/content.php?articleId=467

Page 1: IntroductionThis is the second article of two parts on dotNet threading. In this second part, I will discuss further the synchronization objects in the System.Threading dotNet namespace, thread local storage, COM interoperability and thread states.

Page 2: ReaderWriterLock and Mutex

ReaderWriterLock

Another popular design pattern introduced as a class in the dotNet framework is the ReaderWriterLock. This class allows an unlimited amount of read locks or one write lock, but not both. This allows anyone to read the protected resource, as long as nobody is writing to the protected resource and allows only one thread to write to the protected resource at any one time. Listing 1 presents a sample using the ReaderWriterLock class.

Listing 1: ReaderWriterLock Class

using System;

using System.Threading;

namespace ConsoleApplication9

{

class Class1

{

public Class1()

{

rwlock = new ReaderWriterLock();

val = "Writer Sequence Number is 1";

}

private ReaderWriterLock rwlock;

private string val;

public void Reader()

{

rwlock.AcquireReaderLock(Timeout.Infinite);

Console.WriteLine("Acquired Read Handle: "

"Value = {0}", val);

Thread.Sleep(1);

Console.WriteLine("Releasing Read Handle");

rwlock.ReleaseReaderLock();

}

public void Writer()

{

rwlock.AcquireWriterLock(Timeout.Infinite);

Console.WriteLine("Acquired Write Handle");

int id = rwlock.WriterSeqNum;

Console.WriteLine("Writer Sequence Number "

"is {0}", id);

Thread.Sleep(1);

val = "Writer ";

Thread.Sleep(1);

val += "Sequence ";

Thread.Sleep(1);

val += "Number ";

Thread.Sleep(1);

val += "is ";

Thread.Sleep(1);

val += id;

Console.WriteLine("Releasing Write Handle");

rwlock.ReleaseWriterLock();

}

static void Main(string[] args)

{

Class1 obj = new Class1();

const int n = 1000;

Thread[] reader = new Thread[n];

Thread[] writer = new Thread[10];

for (int i=0;i<n;i++)

{

reader[i] = new Thread(

new ThreadStart(obj.Reader));

if (i<10)

{

writer[i] = new Thread(

new ThreadStart(obj.Writer));

};

}

for (int i=0;i<n;i++)

{

reader[i].Start();

if (i<10)

{

writer[i].Start();

};

}

}

}

}

Mutex

The last synchronization object I’ll present here is the Mutex. The most useful feature of the Mutex class is that it may be named. This allows you to create two Mutex objects in different areas of code without having to share Mutex object instances. As long as the Mutex object instances have the same name, they will synchronize with each other.

You could create the Mutex in two different processes on the same machine and the synchronization crosses the process boundary. Nor do you have to worry about passing the Mutex object in order to share the synchronization object between two threads or methods (see Listing 2).

Listing 2: Mutex Class

using System;

using System.Threading;

namespace ConsoleApplication10

{

class Class1

{

public void ThreadStart()

{

Mutex mutex = new Mutex(false, "MyMutex");

mutex.WaitOne();

Console.WriteLine("Hello");

}

static void Main(string[] args)

{

Class1 obj = new Class1();

Thread thread = new Thread(

new ThreadStart(obj.ThreadStart));

Mutex mutex = new Mutex(true, "MyMutex");

thread.Start();

Thread.Sleep(1000);

Console.WriteLine("Signal");

mutex.ReleaseMutex();

}

}

}

In the above listing, two separate Mutex objects are created, but the Mutex class allows the two instances to interact. The Signal will always precede the Hello in the output of this program.

This is because the Mutex in the thread is created with the lock acquired. The second thread then creates the Mutex without acquiring the lock. The second thread will then wait on the mutex until the main thread releases the mutex a second later.

Page 3: Thread Local Storage and COM Interoperability

Thread Local Storage

The Thread class and System.Threading namespace also contain some methods and classes for realizing thread local storage. Thread local storage is a manner of storing data in a container that is unique to the thread. Many threads could then use the same named container to store their data without concern of collision. Each thread’s local storage is distinct from another thread’s local storage and is only available in the one thread. Listing 3 shows a small sample using the thread-local-storage methods and classes.

Listing 3: Thread Local Storage

using System;

using System.Threading;

namespace ConsoleApplication11

{

class Class1

{

public void ThreadStart()

{

string str1 = "My Cookie " +

Thread.CurrentThread.GetHashCode();

Console.WriteLine("worker thread: {0}",str1);

LocalDataStoreSlot lds =

Thread.GetNamedDataSlot("COOKIE");

Thread.SetData(lds, str1);

Thread.Sleep(1);

LocalDataStoreSlot lds2 =

Thread.GetNamedDataSlot("COOKIE");

string str2 = "";

str2 = (string)Thread.GetData(lds2);

Console.WriteLine("worker thread: {0}",str2);

}

static void Main(string[] args)

{

string str1 = "My Cookie " +

Thread.CurrentThread.GetHashCode();

Console.WriteLine("main thread: {0}", str1);

LocalDataStoreSlot lds =

Thread.AllocateNamedDataSlot("COOKIE");

Thread.SetData(lds, str1);

Class1 obj = new Class1();

Thread thread = new Thread(

new ThreadStart(obj.ThreadStart));

thread.Start();

Thread.Sleep(1);

LocalDataStoreSlot lds2 =

Thread.GetNamedDataSlot("COOKIE");

string str2 = "";

str2 = (string)Thread.GetData(lds2);

Console.WriteLine("main thread: {0}", str2);

}

}

}

You could also create and start more than one thread and the behavior of the thread local storage becomes more obvious. I have played with Win32 thread-local-storage functions and created my own for portability to UNIX, but I have rarely found them very useful. I strongly believe in stateless computing and thread-local-storage contradicts this belief.

COM Interoperability

Now what about those COM apartments? How do these new dotNet threads handle COM apartments? dotNet threads can reside in both single and multithreaded apartments. When a dotNet thread is first started it exists neither in a single-threaded or multithreaded apartment. A static state variable Thread.CurrentThread.Apartment indicates the current apartment type. If you run the code in Listing 4, then the apartment type will be Unknown, as the thread would not have entered an apartment yet.

Listing 4: Threading Model Attributes

using System;

using System.Threading;

namespace ConsoleApplication5

{

class Class1

{

// line output

// // Unknown

// [STAThread] // STA

// [MTAThread] // MTA

public static void Main(String[] args)

{

Console.WriteLine("Apartment State = {0}",

Thread.CurrentThread.ApartmentState);

}

}

}

If you uncomment the line with the STAThread attribute, then the thread set its ApartmentState to STA. If you uncomment the line with the MTAThread attribute, then the thread set its ApartmentState to MTA. This allows control over the apartment type, similar to CoInitializeEx. You can also set the ApartmentState static member directly (see Listing 5).

Listing 5: ApartmentState

using System;

using System.Threading;

namespace ConsoleApplication6

{

class Class1

{

static void Main(string[] args)

{

// Thread.CurrentThread.ApartmentState =

// ApartmentState.STA;

Thread.CurrentThread.ApartmentState =

ApartmentState.MTA;

Console.WriteLine("Apartment State = {0}",

Thread.CurrentThread.ApartmentState);

}

}

}

Setting the ApartmentState property has the same affect as using the STAThread and MTAThread attributes.

There are also class attributes that affect the threading model used by the dotNet framework. The ThreadAffinity and Synchronization class attributes can be used to synchronize access to a class and its instance members.

[ThreadAffinity()]

public class Class1 : ContextBoundObject

[Synchronization()]

public class Class1 : ContextBoundObject

When calling into such classes, the calls are serialized to limit access to the class to one thread at any one time. At this point, these class context attributes are really thin on documentation. So, I’ll save a more in-depth explanation that may be incorrect anyway.

Page 4: Win32 to dotNet

I figured with all this work I’m doing learning dotNet threads that I would leave you with an important resource. Table 1 shows my attempt in converting Win32 functions to dotNet classes and methods.

Table 1: Converting Win32 to dotNet

Win32

dotNet

CreateEvent

new System.Threading.Event

CreateMutex

new System.Threading.Mutex

CreateSemaphore

n/a

CreateThread

new System.Threading.Thread and new System.Threading.ThreadStart

CreateWaitableTimer

new System.Threading.Timer

InitializeCriticalSectiona

EnterCriticalSection

LeaveCriticalSection

DeleteCriticalSection

lock (C#)

System.Threading.Monitor

InterlockedCompareExchange

System.Threading.Interlock.CompareExchange

InterlockedDecrement

System.Threading.Interlock.Decrement

InterlockedExchange

System.Threading.Interlock.Exchange

InterlockedIncrement

System.Threading.Interlock.Increment

OpenEvent

n/a

OpenMutex

new System.Threading.Mutex

OpenSemaphore

n/a

OpenWaitableTimer

n/a

PulseEvent

n/a

ReleaseMutex

System.Threading.Mutex.ReleaseMutex

ReleaseSemaphore

n/a

ResetEvent

System.Threading.AutoResetEvent.Reset or System.Threading.ManualResetEvent.Reset

ResumeThread

System.Threading.Thread.Resume

SetEvent

System.Threading.AutoResetEvent.Set or System.Threading.ManualResetEvent.Set

SetWaitableTimer

n/a

Sleep

System.Threading.Thread.Sleep

SuspendThread

System.Threading.Thread.Suspend

TerminateThread

System.Threading.Thread.Abort

WaitForSingleObject and WaitForSingleObjectEx

System.Threading.Thread.Join or System.Threading.Monitor.Wait or System.Threading.WaitHandle.WaitOne

WaitForMultipleObjects and WaitForMultipleObjects

System.Threading.WaitHandle.WaitAll or System.Threading.WaitHandle.WaitAny

If you were to undertake a project of converting a Win32 application to a dotNet application, then this table could prove very useful. In some cases, a few objects and methods in the dotNet framework could closely emulate a Win32 function.

I had to, on occasion, decide how closely they matched and sometimes decided that a match was not appropriate. As an example, you could create a semaphore with a Mutex object and a counter. But I wouldn’t say it’s a close match, so I didn’t mention these instances. In other cases, I had to decide between two matches.

Page 5: More on Threads and the Interlocked Class

Thread States

The last few topics in this article are really just the few bits of reference information I dug up on dotNet threads. This section describes the states of a thread. The Thread object in the dotNet framework has a property called the ThreadState, which is one of the members of the following enumeration, which I pulled from the dotNET documentation.

public enum ThreadState

{

Running = 0,

SuspendRequested = 2,

Background = 4,

Unstarted = 8,

WaitSleepJoin = 32,

Suspended = 64,

AbortRequested = 128,

Aborted = 256

};

Unfortunately, I have been able to generate ThreadState’s that are not in this enumeration. Specifically, the Stopped ThreadState seems to be missing and is easy to generate. If you check the state of a thread that has run to completion, then the state is marked as Stopped.

What I also found is that it is quite easy to generate dual states. You can be in the AbortRequested state and the WaitSleepJoin state. If you catch the ThreadAbortException and then call Thread.Sleep, then the ThreadState will be “WaitSleepJoin, AbortRequested”, a dual state. The same is true if you are sleeping when the Suspend instance method is called.

Figure 1: State Diagram

Immediately after the call to the Suspend instance method, the ThreadState property reports “SuspendRequested, WaitSleepJoin”, then quickly changes to “WaitSleepJoin, Suspended”. I’ve encountered a few state diagrams that tried to depict the state transitions of dotNet threads. I must say that most are misleading or incomplete.

The biggest problem is that most of the state diagrams did not attempt to account for dual states. My own attempt at the state diagram, I know, is still lacking but much further along then anything else I’ve seen (see Figure 1).

Background Threads

There is still a lot missing from the state diagram. Specifically, what happens when you Suspend(), Wait(), Join(), Sleep(), Abort() a background thread. I’m not going to confuse the diagram to explain these new states. Rather, let me explain that a thread is either a background thread or a foreground thread. Actions on a background thread are equivalent to actions on a foreground thread, except in one respect, which I will explain in the next paragraph.

So, if you attempt to suspend a running background thread, then it will move to the SuspendRequested state, then to the Suspended state and finally back to the Background state, in the same manner as a foreground thread.

The difference between a background thread and a foreground thread is pretty simple. When the last foreground thread of a process is stopped, then the process terminates. There could be zero, 1 or an infinite number of background threads and they have no vote in whether a process terminates or not. So when the last foreground thread stops, then all background threads are also stopped and the process is stopped.

I’ve seen quite a few dot-NET programmers incorrectly use the background thread to mean any thread created using the Thread constructor. The terminology is therefore getting very confusing. The correct meaning of background thread in dotNet framework is a thread that does not have impact on whether a process is terminated.

Thread Safe Objects and Types

Here’s a rather interesting tidbit of news. Many of the dotNet objects and types are thread-safe. The first time I heard that I was rather confused at what it could mean. Does this mean an increment (++) operation on a C# integer is atomic?

I put together a small piece of C# code that launched a thousand threads and incremented and decremented one integer a million times per thread. I structured the code to swap the threads like mad to try and create a race condition that would invalidate the operations on the integer.

I was unsuccessful in generating incorrect results. So, I assume the operation is atomic. But I don’t have any proof (beyond proof-by-example) that it is an atomic operation.

Interlocked

Throughout this article, I have written code that assumes that some operations on C# objects and types are atomic. I would never suggest writing such code in a production environment. In such an environment, you will have to fall back onto our old InterlockedIncrement and InterlockedDecrement friends. In C#, these are in the System.Threading.Interlocked class. The class has two static methods Interlocked.Increment and Interlocked.Decrement. Use them well.

Page 6: Conclusion

I started this trek into dotNet threads for one reason. I wanted to evaluate them as a possible alternative for servers that require a lot of thread programming.

What I found was that dotNet’s Threading namespace is by far the easiest way to write applications that require a lot of thread programming. I didn’t find any performance problems with the dotNet threads, but neither did I find them any faster than other thread libraries available in C++ or Java threads.

For more great programming articles, please visit http://www.devarticles.com. If you have an opinion or question about this article, then please post it at the devArticles developer forum, which is located at http://www.devarticles.com/forum

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有