簡體   English   中英

多線程和監控

[英]Multithreading and Monitoring

我試圖讓多線程的想法更加清晰。 我上了這三個課。

全局變量類

public partial class globes
{
    public bool[] sets = new bool[] { false, false, false };
    public bool boolChanged = false;
    public string tmpStr = string.Empty;
    public int gcount = 0;
    public bool intChanged = false;
    public Random r = new Random();
    public bool gDone = false;
    public bool first = true;
}

下降點

class Driver
    {
        static void Main(string[] args)
        {
            Console.WriteLine("start");

            globes g = new globes();

            Thread[] threads = new Thread[6];
            ParameterizedThreadStart[] pts = new ParameterizedThreadStart[6];

            lockMe _lockme = new lockMe();

            for (int b = 0; b < 3; b++)
            {
                pts[b] = new ParameterizedThreadStart(_lockme.paramThreadStarter);
                threads[b] = new Thread(pts[b]);
                threads[b].Name = string.Format("{0}", b);
                threads[b].Start(b);
            }
        }
    }

然后是我的線程課程

class lockMe
    {
        #region Fields

        private string[] words = new string[] {"string0", "string1", "string2", "string3"};
        private globes g = new globes();
        private object myKey = new object();

        private string[] name = new string[] { String.Empty, String.Empty, String.Empty };

        #endregion

        #region methods

        // first called for all threads
        private void setName(Int16 i)
        {
            Monitor.Enter(myKey);
            {
                try
                {
                    name[i] = string.Format("{0}:{1}", Thread.CurrentThread.Name, g.r.Next(100, 500).ToString());
                }
                finally
                {
                    Monitor.PulseAll(myKey);
                    Monitor.Exit(myKey);
                }
            }
        }

        // thread 1
        private void changeBool(Int16 a)
        {
            Monitor.Enter(myKey);
            {
                try
                {
                    int i = getBools();
                    //Thread.Sleep(3000);
                    if (g.gcount > 5) { g.gDone = true; return; }
                    if (i == 3) resets();
                    else { for (int x = 0; x <= i; i++) { g.sets[x] = true; } }

                    Console.WriteLine("Thread {0} ran through changeBool()\n", name[a]);
                }
                finally
                {
                    Monitor.PulseAll(myKey);
                    Monitor.Exit(myKey);
                }
            }
        }

        // thread 2
        private void changeInt(Int16 i)
        {
            Monitor.Enter(myKey);
            {
                try
                {
                    g.gcount++;
                    //Thread.Sleep(g.r.Next(1000, 3000));
                    Console.WriteLine("Thread {0}: Count is now at {1}\n", name[i], g.gcount);
                }
                finally
                {
                    Monitor.PulseAll(myKey);
                    Monitor.Exit(myKey);
                }
            }
        }

        // thread 3
        private void printString(Int16 i)
        {
            Monitor.Enter(myKey);
            {
                try
                {
                    Console.WriteLine("...incoming...");
                    //Thread.Sleep(g.r.Next(1500, 2500));
                    Console.WriteLine("Thread {0} printing...{1}\n", name[i], words[g.r.Next(0, 3)]);
                }
                finally
                {
                    Monitor.PulseAll(myKey);
                    Monitor.Exit(myKey);
                }
            }
        }

        // not locked- called from within a locked peice
        private int getBools()
        {
            if ((g.sets[0] == false) && (g.sets[1] == false) && (g.sets[2] == false)) return 0;
            else if ((g.sets[0] == true) && (g.sets[1] == false) && (g.sets[2] == false)) return 1;
            else if ((g.sets[2] == true) && (g.sets[3] == false)) return 2;
            else if ((g.sets[0] == true) && (g.sets[1] == true) && (g.sets[2] == true)) return 3;
            else return 99;
        }

        // should not need locks- called within locked statement
        private void resets()
        {
            if (g.first) { Console.WriteLine("FIRST!!"); g.first = false; }
            else Console.WriteLine("Cycle has reset...");
        }

        private bool getStatus()
        {
            bool x = false;

            Monitor.Enter(myKey);
            {
                try
                {
                    x = g.gDone;
                }
                finally
                {
                    Monitor.PulseAll(myKey);
                    Monitor.Exit(myKey);
                }
            }

            return x;
        }

        #endregion

        #region Constructors

        public void paramThreadStarter(object starter)
        {
            Int16 i = Convert.ToInt16(starter);
            setName(i);

            do
            {
                switch (i)
                {
                    default: throw new Exception();
                    case 0:
                        changeBool(i);
                        break;
                    case 1:
                        changeInt(i);
                        break;
                    case 2:
                        printString(i);
                        break;
                }
            } while (!getStatus());

            Console.WriteLine("fin");
            Console.ReadLine();
        }

        #endregion
    }

所以我有幾個問題。 首先-這樣設置全局類會更好嗎? 還是我應該使用帶有屬性的靜態類並以這種方式更改它們? 下一個問題是,運行此線程時,隨機會運行一個線程,脈沖/退出鎖,然后立即退回(有時在下一個線程獲取鎖之前大約5至10次)。 為什么會這樣?

每個線程都被分配了一定的CPU時間,如果您以相同的方式鎖定所有調用並且線程之間的線程優先級相同,那么我懷疑一個特定的線程會比其他線程獲得更多的實際CPU時間。

關於如何使用全局類,這並不重要。 您使用它的方式不會改變它的一種或另一種方式。 使用全局變量是為了測試線程安全性,因此,當多個線程試圖更改共享屬性時 ,最重要的是您必須執行線程安全性。

考慮到只有一個線程可以實際進入,Pulse可能是一個更好的選擇,當您鎖定某項內容時,pulseAll是合適的,因為一旦任務完成並且下次不會鎖定時,您就有任務要做。 在您的方案中,您每次都鎖定,所以執行pulseAll只會浪費cpu,因為您知道它將為下一個請求鎖定。

何時使用靜態類以及為什么必須使它們成為線程安全的常見示例:

public static class StoreManager
{
   private static Dictionary<string,DataStore> _cache = new Dictionary<string,DataStore>(StringComparer.OrdinalIgnoreCase);

   private static object _syncRoot = new object();

   public static DataStore Get(string storeName)
   {
       //this method will look for the cached DataStore, if it doesn't
       //find it in cache it will load from DB.
       //The thread safety issue scenario to imagine is, what if 2 or more requests for 
       //the same storename come in?  You must make sure that only 1 thread goes to the
       //the DB and all the rest wait...

       //check to see if a DataStore for storeName is in the dictionary
       if ( _cache.ContainsKey( storeName) == false )
       {
           //only threads requesting unknown DataStores enter here...

           //now serialize access so only 1 thread at a time can do this...
           lock(_syncRoot)
           {
               if (_cache.ContainsKey(storeName) == false )
               { 
                  //only 1 thread will ever create a DataStore for storeName
                  DataStore ds = DataStoreManager.Get(storeName); //some code here goes to DB and gets a DataStore
                  _cache.Add(storeName,ds);
               }
           }
       }

       return _cache[storeName];
   }
}

真正重要的是,當storeName沒有DataStore時, Get方法僅使調用單線程執行。

Double-Check-Lock:您可以看到第一個lock()發生在if ,因此可以想象3個線程同時運行if ( _cache.ContainsKey(storeName) .. ,現在所有3個線程都輸入了if。現在我們鎖定以便只有一個線程可以進入,現在我們執行完全相同的if語句,只有到達此線程的第一個線程實際上將通過此if語句並獲得DataStore;第一個線程.Add進入DataStore並退出鎖,另一個線程2個線程將無法通過第二次檢查(再次檢查)。

從那時起,任何對該storeName的請求都將獲取緩存的實例。

因此,我們僅在需要它的位置對應用程序進行單線程處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM