繁体   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