utorok, 10 marec 2015 10:21 Written by 2326 times
Rate this item
(0 votes)

C# - Použitie vlákien v oknových aplikáciach

Dnes si ukážeme jednoduchú ukážku, ako sa dajú používať vlákna v oknovej (formulárovej) aplikácii. Program dokáže naštartovať súčasne až dve vlákna navyše. Píšem navyše, pretože samotná formulárová aplikácia naštartuje v rámci OS proces a tým pádom automaticky jedno vlákno, v ktorom beží samotná aplikácia.

Vo formulári sú umiestnené dve tlačidlá s popisom "Start thread 1" a "Start thread 1". Po stlačení každého sa naštartuje jedno vlákno, ktoré inkrementuje svoju vlastnú premennú "i1" a "i2". Zmenu hodnoty možno ihneď pozorovať v labeloch pod každým tlačidlom. Zastavovať a rozbehnúť je možné každé vlákno zvlášť tlačidlami "Stop thread 1" a "Stop thread 2". Aplikácia je v princípe jednoduchá, ide iba o najľahšiu demonštráciu/ukážku práce s vláknami pre programátora. Detailný popis jednotlivého kódu je na dlhšie. V princípe napoviem iba, že využívam v tomto prípade tzv. "callback" funkciu, ktorá spustí asynchrónne napr. SetText1 funkciu pretože nastavenie mnohých vizuálnych vlastností objektov sa vykonáva pomocou tzv. messages - správ, kôli synchronizácii poradia prekreslenia jednotlivých objektov. Aby mohlo takéto vlákno bezpečne nastavovať inkrementovanú hodnotu vo vlastnosti text objektu label. Ďalej stojí za povšimnutie v zdrojovom kóde v metóde DoThread1() použitie statickej metódy triedy vlákna Thread.Sleep(1), ktorou dovolíme na 1 milisekundu urobiť niečo aj ostatným vláknam a premenné "private volatile bool Thread1Running" a "private volatile bool Thread2Running" sú označené kľúčovým slovom "volatile", kde povieme, že chceme, aby mohlo k nim pristupovať viac vlákien. V tomto prípade primárne a ďalšie, nami naštartované tlačidlom. Premenné Thread1Running a Thread2Running používam na kontrolu chodu vlákien - zastavenie.

Po vytvorení oknovej aplikácie si pomenujte a nastavte nasledovné vlastnosti v programe takto:
Assembly name: Threads
Default namespace: Threads
Target framework: .NET Framework 4 Client Profile
Output type: Windows Application

Takže vytvoril som si jednoduchý formulár s menom "Threadform" s ďalšími objektami.
Object: form     Name: Threadform    Text: Threads

Tlačidlá na štart vlákien:

Object: button     Name: button1         Text: Start thread 1
Object: button     Name: button2         Text: Start thread 2
Labely/texty na zobrazenie behu vlákien:
Object: label      Name: label_thread1    Text: 0
Object: label      Name: label_thread2    Text: 0
Tlačidlá na ukončenie vlákien:
Object: button     Name: button3         Text: Stop thread 1
Object: button     Name: button4         Text: Stop thread 2

Tieto objekty si rozmiestnite na formulári do riadku v poradí:
button1     - button2
label_thread1    - label_thread2
button3     - button4

Kód v c#:

using System;
//using System.Collections.Generic;
//using System.ComponentModel;
//using System.Data;
//using System.Drawing;
//using System.Linq;
//using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Threads
{
    public partial class Threadform : Form
    {
        private int i1 = 0;
        private int i2 = 0;

        private volatile bool Thread1Running = false;
        private volatile bool Thread2Running = false;

        private Thread Thread1;
        private Thread Thread2;

        delegate void SetTextCallback1(string text);
        delegate void SetTextCallback2(string text);

        public Threadform()
        {
            InitializeComponent();
        }

        private void StartThread1()
        {
            if (this.Thread1 == null)
            {
                this.Thread1Running = true;
                this.Thread1 = new Thread(new ThreadStart(this.DoThread1));
                this.Thread1.Name = "Thread1";
                this.Thread1.Priority = ThreadPriority.BelowNormal;
                this.Thread1.Start();
            }
        }

        private void EndThread1()
        {
            this.Thread1Running = false;
            if (this.Thread1 != null)
            {
                this.Thread1.Abort();
                this.Thread1 = null;
            }            
        }

        private void StartThread2()
        {
            if (this.Thread2 == null)
            {
                this.Thread2Running = true;
                this.Thread2 = new Thread(new ThreadStart(this.DoThread2));
                this.Thread2.Name = "Thread2";
                this.Thread2.Priority = ThreadPriority.BelowNormal;
                this.Thread2.Start();
            }
        }

        private void EndThread2()
        {
            this.Thread2Running = false;
            if (this.Thread2 != null)
            {
                this.Thread2.Abort();
                this.Thread2 = null;
            }
        }

        private void DoThread1()
        {
            while (this.Thread1Running)
            {
                this.SetText1("Value: " + this.i1.ToString());
                this.i1++;
                Thread.Sleep(1);
            }
            this.SetText1("Thread finished: " + this.i1.ToString());
        }

        private void DoThread2()
        {
            while (this.Thread2Running)
            {
                this.SetText2("Value: " + this.i2.ToString());
                this.i2++;
                Thread.Sleep(1);
            }
            this.SetText2("Thread finished: " + this.i2.ToString());
        }

        private void SetText1(string text)
        {
            if (this.Thread1Running && this.Thread1 != null)
            {
                if (this.label_thread1.InvokeRequired)
                {
                    SetTextCallback1 d1 = new SetTextCallback1(this.SetText1);                    
                    this.label_thread1.Invoke(d1, new object[] { text });
                }
                else
                {
                    label_thread1.Text = text;
                }
            }
        }

        private void SetText2(string text)
        {
            if (this.Thread2Running && this.Thread2 != null)
            {
                if (this.label_thread2.InvokeRequired)
                {
                    SetTextCallback2 d2 = new SetTextCallback2(this.SetText2);
                    this.label_thread2.Invoke(d2, new object[] { text });
                }
                else
                {
                    label_thread2.Text = text;
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            StartThread1();
            this.button1.Text = "Running thread 1";
            this.button3.Enabled = !(this.button1.Enabled = false);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            StartThread2();
            this.button2.Text = "Running thread 2";
            this.button4.Enabled = !(this.button2.Enabled = false);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            this.EndThread1();
            this.button1.Text = "Start thread 1";
            this.button3.Enabled = !(this.button1.Enabled = true);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            this.EndThread2();
            this.button2.Text = "Start thread 2";
            this.button4.Enabled = !(this.button2.Enabled = true);
        }
    }
}

 

Zdrojové súbory sú k dispozícii v prílohe. Aplikáciu preložte (build) a spustite a môžete si skúsť, ako samostatne a nezávisle pracujú vlákna a vy môžete hýbať a pracovať nezávisle s formulárom.

Program bol navrhnutý v prostredí: Microsoft Visual Studio 2010 Ultimate, Microsoft .NET Framework Version 4.0.30319 SP1Rel

Predpokladám základné znalosti s prácou objektami a prácou vo Visual C#.

S pozdravom.

AB

Last modified on utorok, 10 marec 2015 11:06
Alojz Benďák

Autor je administrátor webu a venuje sa programovaniu takmer 20 rokov.



  • prvý kontakt s počítačmi na strednej škole - PMD 1,2,3, PP06, Atari, Sinclair (programovanie v jazyku: strojový kód, basic a pascal):  2 roky
  • na VŠ - jazyky Turbo pascal a Turbo C: 1rok
  • programovanie databázového ekonomického širokoškálneho software v Delphi a C++ Borland (databázy Paradox): 8 rokov v Codex s.r.o. Nitra
  • programovanie webových aplikácií na databázach MySQL a MS SQL server: 1 roky na živnosť (firmy: Hermes Nitra, Schindler v Bratislave)
  • pracoval ako DBA v Homecredit Brno a vo VÚB Bratislava ako vyvojár + optimalizácie v SQL a PL/SQL na Oracle 11g (firma Accenture)
  • programoval programy pre súkromné firmy pre HW na skenovanie povrchov nádrží pre skladovanie tekutých palív (pristroj Leica)
  • momentálne pracuje ako DBA pod Oracle 11g a ako vyvojár vnútropodnikových databázových aplikacií v PHP, SQL, C++, C# a interface v PL/SQL: 8 rokov
  • ďalej programuje s HTML, CSS, Ajax, Javascript, VBA, MS visual C++ a C# malé podporné aplikácie
  • certifikát SQL expert for Oracle 11g   
  • spolupracuje a aktívne učí pre počítačové firmy: Lapis, IVIT v Nitre