Heim > Backend-Entwicklung > C#.Net-Tutorial > C#-Multithread-Programmierung

C#-Multithread-Programmierung

高洛峰
Freigeben: 2016-12-12 14:42:10
Original
1265 Leute haben es durchsucht

1. Gründe für die Verwendung von Threads

1. Threads können verwendet werden, um Code von anderen Codes zu isolieren und die Zuverlässigkeit von Anwendungen zu verbessern.

2. Threads können verwendet werden, um das Codieren zu vereinfachen.

3. Threads können verwendet werden, um eine gleichzeitige Ausführung zu erreichen.

2. Grundkenntnisse

1. Prozess und Thread: Als Grundeinheit des Betriebssystemausführungsprogramms verfügt der Prozess über die Ressourcen des Anwendungsprogramms. Die Ressourcen des Prozesses werden gemeinsam genutzt nach Threads Besitzt keine Ressourcen.

2. Vordergrund-Thread und Hintergrund-Thread: Neue Threads, die über die Thread-Klasse erstellt werden, werden standardmäßig im Vordergrund-Thread angezeigt. Wenn alle Vordergrund-Threads geschlossen sind, werden auch alle Hintergrund-Threads direkt beendet, ohne eine Ausnahme auszulösen.

3. Suspend (Suspend) und Wake-up (Resume): Da die Ausführungsreihenfolge von Threads und die Ausführung des Programms unvorhersehbar sind, ist die Verwendung von Suspend und Wake-up in der Praxis anfällig für Deadlocks Anwendungen sollte es so wenig wie möglich verwendet werden.

4. Blockierender Thread: Beitreten, blockiert den aufrufenden Thread, bis der Thread beendet wird.

5. Beenden Sie den Thread: Abort: Lösen Sie eine ThreadAbortException-Ausnahme aus, um den Thread zu beenden. Interrupt: Löst eine ThreadInterruptException-Ausnahme aus, um den Thread zu beenden, und die Ausführung kann durch Abfangen der Ausnahme fortgesetzt werden.

6. Thread-Priorität: AboveNormal BelowNormal Highest Lowe Normal, der Standardwert ist Normal.

3. Die Verwendung von Threads

Thread-Funktionen werden durch Delegation übergeben und können ohne Parameter oder mit Parametern erfolgen (nur ein Parameter kann in einer Klasse oder Struktur gekapselt werden).

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(TestMethod));
            Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
            t1.IsBackground = true;
            t2.IsBackground = true;
            t1.Start();
            t2.Start("hello");
            Console.ReadKey();
        }

        public static void TestMethod()
        {
            Console.WriteLine("不带参数的线程函数");
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
        }
    } 
}
Nach dem Login kopieren

4. Thread-Pool

Da die Erstellung und Zerstörung von Threads einen gewissen Overhead erfordert, führt eine übermäßige Nutzung von Threads zu einer Verschwendung von Speicherressourcen Das Konzept des Thread-Pools wurde eingeführt. Der Thread-Pool verwaltet eine Anforderungswarteschlange. Der Thread-Pool-Code extrahiert die Aufgabe aus der Warteschlange und delegiert sie dann zur Ausführung an einen Thread im Thread-Pool. Der Thread wird nicht sofort nach der Ausführung zerstört, sodass Aufgaben im Thread-Pool ausgeführt werden können Der Aufwand für die Erstellung und Zerstörung von Threads kann reduziert werden.

Der Thread-Pool-Thread ist standardmäßig der Hintergrund-Thread (IsBackground).

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            //将工作项加入到线程池队列中,这里可以传递一个线程参数
            ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
            Console.ReadKey();
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine(datastr);
        }
    }
}
Nach dem Login kopieren

5. Task-Klasse

Es ist sehr einfach, die QueueUserWorkItem()-Methode von ThreadPool zu verwenden, um eine asynchrone Thread-Ausführung zu initiieren, aber das größte Problem bei dieser Methode ist, dass dies der Fall ist Es gibt keinen integrierten Mechanismus, um zu wissen, wann der Vorgang abgeschlossen ist. Gibt es einen integrierten Mechanismus, um nach Abschluss des Vorgangs einen Rückgabewert abzurufen? Zu diesem Zweck können Sie die Task-Klasse von System.Threading.Tasks verwenden.

Erstellen Sie ein Task-Objekt und übergeben Sie den Rückgabetyp einer Operation für den generischen TResult-Parameter.

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            t.Wait();
            Console.WriteLine(t.Result);
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //结果太大,抛出异常
            return sum;
        }
    }
}
Nach dem Login kopieren

Wenn eine Aufgabe abgeschlossen ist, wird automatisch eine neue Aufgabe gestartet.
Nachdem eine Aufgabe abgeschlossen ist, kann sie eine andere Aufgabe starten. Der vorherige Code wird unten neu geschrieben, ohne dass Threads blockiert werden.

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            //t.Wait();
            Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result));
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //结果溢出,抛出异常
            return sum;
        }
    }
}
Nach dem Login kopieren

6. Asynchrone Ausführung von Delegaten

Asynchrone Aufrufe von Delegaten: BeginInvoke() und EndInvoke()

namespace Test
{
    public delegate string MyDelegate(object data);
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate mydelegate = new MyDelegate(TestMethod);
            IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");

            //异步执行完成
            string resultstr = mydelegate.EndInvoke(result);
        }

        //线程函数
        public static string TestMethod(object data)
        {
            string datastr = data as string;
            return datastr;
        }

        //异步回调函数
        public static void TestCallback(IAsyncResult data)
        {
            Console.WriteLine(data.AsyncState);
        }
    }
}
Nach dem Login kopieren

7. Thread-Synchronisation

 1) Atomare Operation (verriegelt): Alle Methoden führen eine atomare Lese- oder Schreiboperation aus.

2) lock()-Anweisung: Vermeiden Sie das Sperren des öffentlichen Typs, da die Instanz sonst außerhalb des Bereichs der Codekontrolle liegt. Definieren Sie ein privates Objekt zum Sperren.

 3) Monitor implementiert die Thread-Synchronisierung

  Der Erwerb und die Freigabe exklusiver Sperren werden durch Monitor.Enter() und Monitor.Exit() erreicht. Nach dem Erwerb sind die Ressourcen exklusiv und nichts anderes Threads dürfen darauf zugreifen.

Es gibt auch eine TryEnter-Methode, die nicht blockiert und wartet, wenn die Ressource nicht angefordert werden kann. Sie können ein Timeout festlegen und false zurückgeben, wenn sie nicht abgerufen werden kann.

 4) ReaderWriterLock

  Wenn der Ressourcenvorgang mehr Lesen und weniger Schreiben umfasst, ist die Lesevorgangssperre eine gemeinsame Sperre, um die Ressourcennutzung zu verbessern, sodass mehrere Threads die Ressource lesen können Gleichzeitig ist der Schreibvorgang eine exklusive Sperre und es darf nur ein Thread ausgeführt werden.

5) Die Ereignisklasse implementiert die Synchronisierung

Die Ereignisklasse verfügt über zwei Zustände, den Beendigungszustand und den Nicht-Beendigungszustand. Durch Aufrufen von WaitOne im Beendigungszustand kann der Zeitstatus durchgesetzt werden Set ist der Endzustand.

1) AutoResetEvent (automatisches Reset-Ereignis)

2) ManualResetEvent (manuelles Reset-Ereignis)

6) Semaphore (Semaphore)

Signal Die Menge beträgt Eine vom Kernelobjekt verwaltete int-Variable. Wenn sie 0 ist, ist der Thread blockiert. Wenn der wartende Thread auf einem Semaphor entsperrt ist, beträgt die Semaphorzahl +1.

Der Thread verringert den Semaphor um 1 durch WaitOne und erhöht den Semaphor um 1 durch Release. Die Verwendung ist sehr einfach.

7) Mutex (Mutex)

Exklusive Ressource, die Verwendung ist ähnlich wie bei Semaphore.

8) Prozessübergreifende Synchronisierung

Eine Synchronisierung auf Systemebene kann durch Festlegen des Namens des Synchronisierungsobjekts erreicht werden. Verschiedene Anwendungen identifizieren unterschiedliche Synchronisierungsobjekte anhand des Namens des Synchronisierungsobjekts.


Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage