Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

public void Thread1DoWork()

{

bool mustDoWork = true; Thread.Sleep(100); while (mustDoWork)

{

lock (resourceA)

{

Thread.Sleep(100);

if (Monitor.TryEnter(resourceB, 0))

{

output += "T1#"; mustDoWork = false; Monitor.Exit(resourceB);

}

}

if (mustDoWork) Thread.Yield();

}

}

public void Thread2DoWork()

{

Thread.Sleep(100); lock (resourceB)

{

Thread.Sleep(100); lock (resourceA)

{

output += "T2#";

}

}

}

}

Note that this workaround relies on thread2 being stubborn about its locks and thread1 being willing to yield, such that thread2 always take precedence. Also note that thread1 has to redo the work it did after locking resource A, when it yields. Therefore be careful when implementing this approach with more than one yielding thread, as you'll then run the risk of entering a so-called livelock - a state which would occur if two threads kept doing the first bit of their work and then yield mutually, starting over repeatedly.

Section 112.5: Simple Complete Threading Demo

class Program

{

static void Main(string[] args)

{

//Create 2 thread objects. We're using delegates because we need to pass

//parameters to the threads.

var thread1 = new Thread(new ThreadStart(() => PerformAction(1)));

var thread2 = new Thread(new ThreadStart(() => PerformAction(2)));

//Start the threads running thread1.Start();

//NB: as soon as the above line kicks off the thread, the next line starts;

//even if thread1 is still processing.

thread2.Start();

//Wait for thread1 to complete before continuing thread1.Join();

//Wait for thread2 to complete before continuing

GoalKicker.com – C# Notes for Professionals

573

thread2.Join();

Console.WriteLine("Done"); Console.ReadKey();

}

// Simple method to help demonstrate the threads running in parallel. static void PerformAction(int id)

{

var rnd = new Random(id);

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

{

Console.WriteLine("Thread: {0}: {1}", id, i); Thread.Sleep(rnd.Next(0, 1000));

}

}

}

Section 112.6: Creating One Thread Per Processor

Environment.ProcessorCount Gets the number of logical processors on the current machine.

The CLR will then schedule each thread to a logical processor, this theoretically could mean each thread on a di erent logical processor, all threads on a single logical processor or some other combination.

using System;

using System.Threading;

class MainClass { static void Main() {

for (int i = 0; i < Environment.ProcessorCount; i++) { var thread = new Thread(Secondary); thread.Start(i);

}

}

static void Secondary(object threadNumber) { System.Console.WriteLine("Hello World from thread: " + threadNumber);

}

}

Section 112.7: Simple Complete Threading Demo using Tasks

class Program

{

static void Main(string[] args)

{

// Run 2 Tasks.

var task1 = Task.Run(() => PerformAction(1)));

var task2 = Task.Run(() => PerformAction(2)));

// Wait (i.e. block this thread) until both Tasks are complete.

Task.WaitAll(new [] { task1, task2 });

Console.WriteLine("Done"); Console.ReadKey();

}

GoalKicker.com – C# Notes for Professionals

574

// Simple method to help demonstrate the threads running in parallel. static void PerformAction(int id)

{

var rnd = new Random(id);

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

{

Console.WriteLine("Task: {0}: {1}", id, i); Thread.Sleep(rnd.Next(0, 1000));

}

}

}

Section 112.8: Deadlocks (two threads waiting on each other)

A deadlock is what occurs when two or more threads are waiting for each other to complete or to release a resource in such a way that they wait forever.

A typical scenario of two threads waiting on each other to complete is when a Windows Forms GUI thread waits for a worker thread and the worker thread attempts to invoke an object managed by the GUI thread. Observe that with this code example, clicking button1 will cause the program to hang.

private void button1_Click(object sender, EventArgs e)

{

Thread workerthread= new Thread(dowork); workerthread.Start(); workerthread.Join();

// Do something after

}

private void dowork()

{

// Do something before

textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));

// Do something after

}

workerthread.Join() is a call that blocks the calling thread until workerthread completes. textBox1.Invoke(invoke_delegate) is a call that blocks the calling thread until the GUI thread has processed invoke_delegate, but this call causes deadlocks if the GUI thread is already waiting for the calling thread to complete.

To get around this, one can use a non-blocking way of invoking the textbox instead:

private void dowork()

{

// Do work

textBox1.BeginInvoke(new Action(() => textBox1.Text = "Some Text"));

// Do work that is not dependent on textBox1 being updated first

}

However, this will cause trouble if you need to run code that is dependent on the textbox being updated first. In that case, run that as part of the invoke, but be aware that this will make it run on the GUI thread.

private void dowork()

{

// Do work

textBox1.BeginInvoke(new Action(() => { textBox1.Text = "Some Text";

GoalKicker.com – C# Notes for Professionals

575

//Do work dependent on textBox1 being updated first,

//start another worker thread or raise an event

}));

// Do work that is not dependent on textBox1 being updated first

}

Alternatively start af whole new thread and let that one do the waiting on the GUI thread, so that workerthread might complete.

private void dowork()

{

// Do work

Thread workerthread2 = new Thread(() =>

{

textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));

//Do work dependent on textBox1 being updated first,

//start another worker thread or raise an event

}); workerthread2.Start();

// Do work that is not dependent on textBox1 being updated first

}

To minimize the risk of running into a deadlock of mutual waiting, always avoid circular references between threads when possible. A hierarchy of threads where lower-ranking threads only leave messages for higher-ranking threads and never waiting on them will not run into this kind of issue. However, it would still be vulnerable to deadlocks based on resource locking.

Section 112.9: Explicit Task Parallism

private static void explicitTaskParallism()

{

Thread.CurrentThread.Name = "Main";

// Create a task and supply a user delegate by using a lambda expression.

Task taskA = new Task(() => Console.WriteLine($"Hello from task {nameof(taskA)}.")); Task taskB = new Task(() => Console.WriteLine($"Hello from task {nameof(taskB)}."));

//Start the task. taskA.Start(); taskB.Start();

//Output a message from the calling thread.

Console.WriteLine("Hello from thread '{0}'.",

Thread.CurrentThread.Name);

taskA.Wait(); taskB.Wait(); Console.Read();

}

Section 112.10: Implicit Task Parallelism

private static void Main(string[] args)

{

var a = new A(); var b = new B();

//implicit task parallelism

Parallel.Invoke(

() => a.DoSomeWork(),

GoalKicker.com – C# Notes for Professionals

576