探讨:别人的代码&自己N天前写的代码(多线程)

编程工作中:

有个痛点叫做:别人的代码&自己N天前写的代码。

痛点的症状是:一时间找不到南北,相应花费时间才能缕清具体的来龙去脉。

当前比较多的解决办法是,注释规范化。这个有督促还好,若是没有,很多时候就没有注释了。这就造成了代码不容易看懂。
是否还有别的?这里说一个使用多线程的解决办法。

多线程的特点是根据线程间通讯,来实现多个线程协同工作。

一个生活中通俗的例子就是去饭馆吃饭:点菜,做菜和上菜。分成三个线程,各自工作,是不受影响的。
以下是这三个线程的状态:
点菜:等待客人,记录客人点的菜
做菜:查看点菜记录,做菜,做菜完成
上菜:等待做菜完成,上菜

点菜,把客户要点的菜记录好;做菜,读取客户的点菜记录来做菜;上菜,在有做好的菜就上菜。

在实际的多线程中,点菜,做菜和上菜,这样的关系是比较少。如果都是这样的话,那就简单多了。

实际的多线程会是,个别线程会需要多个线程满足条件后才能继续后续的工作,在后续的工作还会需要其他线程满足条件才能工作。这样,若是没有做好注解,就不好去理解具体的工作流程。

这里的解决办法是:增加线程的状态。

设置状态代码为:

uDdkr_等待客人();//“u”为“update”首字母;“Ddkr”为“等待客人”的首字母,若那天VS能识别中文首字母就不需要加“Ddkr”来做引导了。

判断线程到了哪一个状态的代码为:

if (点菜.sDdkr_等待客人)
{
              
}

点菜的部分类代码为:

namespace WCFState
{
	public partial class 点菜
	{
		#region   点菜状态
		public enum S
		{
			正在准备中,
			等待客人,
			记录客人点的菜,
		}
		private static S s状态;
		public static S 点菜状态
		{
			get { return s状态; }
		}
		#region   判断当前的状态
		public static bool sZzzbz_正在准备中
		{
			get
				{
				if (s状态 == S.正在准备中)
					return true;
				return false;
				}
		}
		public static bool sDdkr_等待客人
		{
			get
				{
				if (s状态 == S.等待客人)
					return true;
				return false;
				}
		}
		public static bool sJlkrdd_记录客人点的菜
		{
			get
				{
				if (s状态 == S.记录客人点的菜)
					return true;
				return false;
				}
		}
		#endregion
		#region   更新状态
		private static void uZzzbz_正在准备中()
		{
			s状态 = S.正在准备中;
		}
		private static void uDdkr_等待客人()
		{
			s状态 = S.等待客人;
		}
		private static void uJlkrdd_记录客人点的菜()
		{
			s状态 = S.记录客人点的菜;
		}
		#endregion
		#endregion
	}
}

以上的代码若是要手写,确实头大,故做了个自动生成控件,正常情况下,创建一个自定义控件:WCFState,把以下代码复制粘贴到“WCFState.cs”里面,再改下命名空间,该自定义控件就能拿来用。具体生成的代码为:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WCFStateDemo
{
    public partial class WCFState : UserControl
    {
        private List<string> listData = new List<string>();

        private DataGridView dgvAutoStatus;
        private RichTextBox txtStatus;

        private bool isGenCode = false;//是否是生成代码状态,不是生成代码状态则为列表实时显示状态

        public WCFState()
        {
            InitializeComponent();
        }
        private void WCFState_Load(object sender, EventArgs e)
        {
           Init();
        }
        #region 界面初始化
        private void Init()
        {
            if (isGenCode)
            {
                #region 添加输入框
                txtStatus = new RichTextBox();
                txtStatus.Dock = DockStyle.Fill;
                txtStatus.Location = new Point(0, 0);
                txtStatus.Multiline = true;
                txtStatus.Name = "txtStatus";
                txtStatus.Size = new Size(461, 487);
                txtStatus.Font = new Font("宋体", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(134)));
                #region 创建右键菜单
                ToolStripMenuItem cmsOpenStatusTxt = new ToolStripMenuItem();
                cmsOpenStatusTxt.Name = "cmsOpenStatusTxt";
                cmsOpenStatusTxt.Size = new Size(180, 30);
                cmsOpenStatusTxt.Text = "打开TXT状态文件";
                cmsOpenStatusTxt.Click += new EventHandler(cmsOpenStatusTxt_Click);
                ToolStripMenuItem cmsGenStatusCode = new ToolStripMenuItem();
                cmsGenStatusCode.Name = "cmsGenStatusCode";
                cmsGenStatusCode.Size = new Size(180, 30);
                cmsGenStatusCode.Text = "生成状态相关代码";
                cmsGenStatusCode.Click += new EventHandler(cmsGenStatusCode_Click);
                ContextMenuStrip cmsAddStatus = new ContextMenuStrip(components);
                cmsAddStatus.Name = "cmsAddStatus";
                cmsAddStatus.Size = new Size(180, 50);
                cmsAddStatus.Items.AddRange(new ToolStripItem[] { cmsOpenStatusTxt, cmsGenStatusCode });
                #endregion
                txtStatus.ContextMenuStrip = cmsAddStatus;
                txtStatus.TabIndex = 1;
                Controls.Add(txtStatus);
                txtStatus.Text += "//生成时会同时把当前生成内容以TXT文件保存在相同的目录下,右键“打开TXT状态文件”时选择这个文件可以直接加载进来\r\n";
                txtStatus.Text += "//增加规则是:一个类名和其状态名称是一行,使用逗号隔开\r\n";
                txtStatus.Text += "//范例:第一行为命名空间,之后每行为:类名+“中文逗号”+状态+“中文逗号”+状态+“中文逗号”+状态 等等\r\n";
                txtStatus.Text += "//使用一个生活小示例,如下:(“//”为注释符,在生成时会忽略)\r\n";
                txtStatus.Text += "//namespace WCFStateDemo\r\n";
                txtStatus.Text += "//点菜,正在准备中,等待客人,记录客人点的菜\r\n";
                txtStatus.Text += "//做菜,正在准备中,查看点菜记录,做菜,做菜完成\r\n";
                txtStatus.Text += "//上菜,正在准备中,等待做菜完成,上菜\r\n";
                txtStatus.Text += "//添加好后,右键生成,会在选择的目录下生成相应的状态类,及把本次生成的内容写到TXT文档\r\n";
                txtStatus.Text += ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,";
                #endregion
            }
            else
            {
                #region 添加显示的列表
                dgvAutoStatus = new DataGridView();
                dgvAutoStatus.AllowUserToAddRows = false;
                dgvAutoStatus.AllowUserToDeleteRows = false;
                dgvAutoStatus.BackgroundColor = SystemColors.ButtonHighlight;
                dgvAutoStatus.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
                dgvAutoStatus.Dock = DockStyle.Fill;
                dgvAutoStatus.Location = new Point(0, 0);
                dgvAutoStatus.ReadOnly = true;
                dgvAutoStatus.ClearSelection();//取消默认选中效果
                dgvAutoStatus.Name = "dgvAutoStatus";
                dgvAutoStatus.RowHeadersVisible = false;
                dgvAutoStatus.RowTemplate.Height = 23;
                dgvAutoStatus.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
                dgvAutoStatus.Size = new Size(461, 487);
                dgvAutoStatus.TabIndex = 1;
                Controls.Add(dgvAutoStatus);
                #endregion
                #region 创建定时器
                Timer timeRefresh = new Timer(components);
                timeRefresh.Enabled = true;
                timeRefresh.Interval = 600;
                timeRefresh.Tick += new EventHandler(this.timeRefresh_Tick);
                #endregion
            }
        }
        #endregion
        #region 生成状态代码
        private void cmsOpenStatusTxt_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "txt文本格式(*.txt)|*.txt";
            dialog.ValidateNames = true;
            dialog.CheckPathExists = true;
            dialog.CheckFileExists = true;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                OpenStatusTxt(dialog.FileName);
            }
        }
        private void OpenStatusTxt(string strFileFullPath)
        {
            StreamReader sr = new StreamReader(strFileFullPath, Encoding.Default);
            txtStatus.Text = sr.ReadToEnd();//给编辑器添加内容
            sr.Close();
        }
        private void cmsGenStatusCode_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "请选择文件夹";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (string.IsNullOrEmpty(dialog.SelectedPath))
                {
                    MessageBox.Show(this, "文件夹路径不能为空", "提示");
                    return;
                }
                strFileFolderPath = dialog.SelectedPath;
                writeStateCode();
            }
        }
        private static string strFileFolderPath = "";
        private static string strNamespace = "";
        private void writeStateCode()
        {
            //写入文件操作
            string strBackupsFileName = strFileFolderPath + "\\生成数据备份" +
                DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + 
                DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + 
                DateTime.Now.Millisecond.ToString() + ".txt";
            TextWriter twBackups = new StreamWriter(new BufferedStream(new FileStream(strBackupsFileName , 
                FileMode.Create, FileAccess.Write)),Encoding.GetEncoding("gb2312"));
            twBackups.WriteLine("//" + strFileFolderPath);//每行都添加,生成数据备份
            try
            {
                List<string> strNames = new List<string>();
                strNamespace = "";
                foreach (string strLines in txtStatus.Lines)
                {
                    //当一行的字符少于3个和前面两个字符是\\时跳过
                    if (strLines.Length < 3 || strLines.Substring(0, 2).Equals("//"))
                        continue;
                    if (strLines.Contains("namespace"))
                    {
                        strNamespace = strLines;//记录命名空间
                        twBackups.WriteLine(strLines);//每行都添加,生成数据备份
                        continue;
                    }
                    else if (strNamespace.Length <= 0)
                    {
                        continue;//当命名空间没有读到,则跳过
                    }
                    string[] strLine = strLines.Split(',');
                    writeStateCodeClass(strLine);//生成一个类文件
                    twBackups.WriteLine(strLines);//每行都添加,生成数据备份
                    strNames.Add(strLine[0]);//添加类名,为后面生成主控件添加的数据
                }
                if (strNamespace.Length <= 0)
                {
                    MessageBox.Show("命名空间未找到,请参考示例代码来生成");
                    return;
                }
                twBackups.WriteLine("//>>>>>>在放该自定义控件的界面上增加listDataAdd(例如以下)才能实时刷新状态>>>>>>>>");
                foreach (string item in strNames)
                {
                    twBackups.WriteLine("//控件Name.listDataAdd(\""+ strNamespace.Replace("namespace ", "") + "." + item + "\");");
                }
                twBackups.Flush();
            }
            finally
            {
                twBackups.Close();
            }
            //使用记事本打开文档
            System.Diagnostics.Process.Start("notepad.exe", strBackupsFileName);
        }
        /// <summary>
        /// 解析一个工位的状态
        /// </summary>
        /// <param name="tw">命名空间</param>
        /// <param name="strLine">类名称和状态名数组</param>
        private void writeStateCodeClass( string[] strLine)
        {
            if (strLine.Length < 3) return;

            TextWriter tw = new StreamWriter(new BufferedStream(new FileStream(
                strFileFolderPath + "\\"+ strLine[0] + ".cs", FileMode.Create, FileAccess.Write)),
                Encoding.GetEncoding("gb2312"));

            tw.WriteLine(strNamespace);//设置命名空间
            tw.WriteLine("{");
            tw.WriteLine("\tpublic partial class " + strLine[0]);
            tw.WriteLine("\t{");
            tw.WriteLine("\t\t#region   " + strLine[0] + "状态");
            //写入枚举
            tw.WriteLine("\t\tpublic enum S");
            tw.WriteLine("\t\t{");
            foreach (string item in strLine)
            {
                if (item.Equals(strLine[0]) || item.Length < 3) continue;//跳过第一个值,少于两个字符也跳过

                tw.WriteLine("\t\t\t" + item + ",");
            }
            tw.WriteLine("\t\t}");
            //写入属性
            tw.WriteLine("\t\tprivate static S s状态;");
            tw.WriteLine("\t\tpublic static S " + strLine[0] + "状态");
            tw.WriteLine("\t\t{");
            tw.WriteLine("\t\t\tget { return s状态; }");
            tw.WriteLine("\t\t}");
            tw.WriteLine("\t\t#region   判断当前的状态");
            //做判断用
            foreach (string item in strLine)
            {
                if (item.Equals(strLine[0]) || item.Length < 3) continue;//跳过第一个值,少于两个字符也跳过

                tw.WriteLine("\t\tpublic static bool s" + GetSpellCode(item) + "_" + item);
                tw.WriteLine("\t\t{");
                tw.WriteLine("\t\t\tget");
                tw.WriteLine("\t\t\t\t{");
                tw.WriteLine("\t\t\t\tif (s状态 == S." + item + ")");
                tw.WriteLine("\t\t\t\t\treturn true;");
                tw.WriteLine("\t\t\t\treturn false;");
                tw.WriteLine("\t\t\t\t}");
                tw.WriteLine("\t\t}");
            }
            tw.WriteLine("\t\t#endregion");
            tw.WriteLine("\t\t#region   更新状态");
            //做设置用
            foreach (string item in strLine)
            {
                if (item.Equals(strLine[0]) || item.Length < 3) continue;//跳过第一个值,少于两个字符也跳过

                tw.WriteLine("\t\tprivate static void u" + GetSpellCode(item) + "_" + item + "()");
                tw.WriteLine("\t\t{");
                tw.WriteLine("\t\t\ts状态 = S." + item + ";");
                tw.WriteLine("\t\t}");
            }
            tw.WriteLine("\t\t#endregion");
            tw.WriteLine("\t\t#endregion");
            tw.WriteLine("\t}");
            tw.WriteLine("}");
            //生成文件
            tw.Flush();
            tw.Close();
        }
        #endregion
        #region 状态刷新
        private void timeRefresh_Tick(object sender, EventArgs e)
        {
            if (listData.Count > 0)
                RefreshData();
        }
        private int iOneInit = 0;//是否是第一次读取数据
        public void RefreshData()
        {
            if (isGenCode)//是增加代码的状态不能刷新数据,因为dgvAutoStatus没有加载
                return;
            
            if (iOneInit == 0)
            {
                DataTable dt = new DataTable();
                dt.Columns.Add("名称", Type.GetType("System.String"));
                dt.Columns.Add("状态", Type.GetType("System.String"));
                foreach (string item in listData)
                {
                    DataRow mRow = dt.NewRow();
                    Type type = Type.GetType(item);
                    string[] items = item.Split('.');
                    object oValue = type.GetProperty(items[1] + "状态").GetValue(type, null);
                    mRow[0] = items[1] + "的状态";//状态名称赋值
                    mRow[1] = oValue.ToString();
                    dt.Rows.Add(mRow);
                }
                dgvAutoStatus.DataSource = dt;
                dgvAutoStatus.ClearSelection();
                dgvAutoStatus.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                dgvAutoStatus.Columns[1].FillWeight = 120;
                iOneInit = 1;
                //禁止排序
                foreach (DataGridViewColumn item in dgvAutoStatus.Columns)
                {
                    item.SortMode = DataGridViewColumnSortMode.NotSortable;
                }
            }
            else
            {
                if (dgvAutoStatus == null || dgvAutoStatus.Rows.Count == 0) return;
                int i = 0;//列表匹配和更新用的下标
                foreach (string item in listData)
                {
                    Type type = Type.GetType(item);
                    string[] items = item.Split('.');
                    object oValue = type.GetProperty(items[1] + "状态").GetValue(type, null);
                    //当前的状态和显示的状态不一致才更新状态
                    if (!(oValue.ToString()).Equals(dgvAutoStatus.Rows[i].Cells[1].Value.ToString()))
                        dgvAutoStatus.Rows[i].Cells[1].Value = oValue.ToString();

                    i++;
                }
            }
        }
        #endregion
        #region 状态列表添加刷新的内容
        /// <summary>
        /// 状态列表添加刷新的内容
        /// </summary>
        /// <param name="name"></param>
        public void listDataAdd(string name)
        {
            listData.Add(name);
        }
        #endregion
        #region 获取汉字首字母
        /// <summary>
        /// 获取汉字首字母
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetSpellCode(string str)
        {
            string strTemp = "";
            for (int i = 0; i < str.Length; i++)
            {
                if (i == 0)
                    strTemp += GetCharSpellCode(str.Substring(i, 1)).ToUpper();
                else
                    strTemp += GetCharSpellCode(str.Substring(i, 1));
                if (strTemp.Length >= 6)
                    return strTemp;
            }
            return strTemp;
        }
        /// <summary>
        /// 获取1个汉字的首字母
        /// </summary>
        /// <param name="strChar">一个字符</param>
        /// <returns></returns>
        public static string GetCharSpellCode(string strChar)
        {
            long iChar;
            byte[] ZW = System.Text.Encoding.Default.GetBytes(strChar);
            if (ZW.Length == 1)
                return strChar.ToLower();
            else
                iChar = ((short)ZW[0]) * 256 + (short)ZW[1];

            if (iChar >= 45217 && iChar <= 45252)
                return "a";
            else if (iChar >= 45253 && iChar <= 45760)
                return "b";
            else if (iChar >= 45761 && iChar <= 46317)
                return "c";
            else if (iChar >= 46318 && iChar <= 46825)
                return "d";
            else if (iChar >= 46826 && iChar <= 47009)
                return "e";
            else if (iChar >= 47010 && iChar <= 47296)
                return "f";
            else if (iChar >= 47297 && iChar <= 47613)
                return "g";
            else if (iChar >= 47614 && iChar <= 48118)
                return "h";
            else if (iChar >= 48119 && iChar <= 49061)
                return "j";
            else if (iChar >= 49062 && iChar <= 49323)
                return "k";
            else if (iChar >= 49324 && iChar <= 49895)
                return "l";
            else if (iChar >= 49896 && iChar <= 50370)
                return "m";
            else if (iChar >= 50371 && iChar <= 50613)
                return "n";
            else if (iChar >= 50614 && iChar <= 50621)
                return "o";
            else if (iChar >= 50622 && iChar <= 50905)
                return "p";
            else if (iChar >= 50906 && iChar <= 51386)
                return "q";
            else if (iChar >= 51387 && iChar <= 51445)
                return "r";
            else if (iChar >= 51446 && iChar <= 52217)
                return "s";
            else if (iChar >= 52218 && iChar <= 52697)
                return "t";
            else if (iChar >= 52698 && iChar <= 52979)
                return "w";
            else if (iChar >= 52980 && iChar <= 53688)
                return "x";
            else if (iChar >= 53689 && iChar <= 54480)
                return "y";
            else if (iChar >= 54481 && iChar <= 55289)
                return "z";
            return strChar;
        }
        #endregion
    }
}

以上为自定义控件的代码,在使用时还相应在自定义控件所在的页面的“Load”里面增加:

//>>>>>>在放该自定义控件的界面上增加listDataAdd(例如以下)才能实时刷新状态>>>>>>>>
//控件Name.listDataAdd("点菜");
//控件Name.listDataAdd("做菜");
//控件Name.listDataAdd("上菜");

这样自定义控件的:

private bool isGenCode = false;//是否是生成代码状态,不是生成代码状态则为列表实时显示状态

即:isGenCode = false;就可以在状态改变后列表实时显示

界面预览。

生成代码状态时界面截图(isGenCode = true):

实时显示流程对应的状态界面截图(isGenCode = false):

这样把每个线程都创建好相应的状态,在相应的位置设置好相应状态。这样既能做看多线程运行到那个位置,还能直接拿这个状态来做线程间通讯。

在没写这篇之前,觉得这种方式可以解决70%代码不好梳理的痛点。写完后发现似乎不到60%,若你有更好的办法,欢迎评论。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值