一、LogServer程序部分讲解

1.LogServer程序代码

    public class LogServer
    {
        private ConcurrentQueue<LogInfo> logQueue = new ConcurrentQueue<LogInfo>();

        private Task task;

        public static LogServer instance { get; private set; } = new LogServer();
        private LogServer()
        {
            task = Task.Run(() => doWork());
            //循环等待任务启动,不启动这里是不是一直阻塞
            while (true)
            {
                if(task.Status==TaskStatus.Running)
                {
                    break;
                }
                Thread.Sleep(200);
            }
        }

        private void doWork()
        {
            while (true)
            {
                int faiCount = 0;
                if (logQueue.Count > 0)
                {   //失败次数
                   
                    //如果队列元素大于0,取出数组
                    //获取一个日志 取出来数据
                    logQueue.TryDequeue(out LogInfo logInfo);
                    Debug.WriteLine(logInfo.line);

                LogStart: try
                    {   //using 代码块  创建对象  创建对象完成后自动调用Dispose方法
                        //创建流对象 logInfo。path,地址,true,Encoding.UTF8
                        using (StreamWriter sw = new StreamWriter(logInfo.path, true, Encoding.UTF8))
                        {
                            sw.WriteLine(logInfo.line);
                           }


                    }
                    catch (Exception)
                    {
                        faiCount++;
                        if (faiCount < 20)
                        {
                            Thread.Sleep(100);
                            goto LogStart;
                        }
                        else
                        {   //提醒用户写入失败,弹窗也可以,三色灯也可以
                            Debug.WriteLine("写入日志失败");
                        }
                    }
                   
                    
                   
                }
            }
        }
        private string logPath = @"D:\log\";
        public void writeLog(string line)
        {   //创建日志文件包含路径和文件名
            string FileName = $"{logPath}{DateTime.Now:yyyy-MM}\\{DateTime.Now:yyyy-MM-dd}_LOGINFOR.txt";
            Debug.WriteLine(FileName);

            //创建目录
            if (!Directory.Exists(FileName.Substring(0, FileName.LastIndexOf("\\"))))
            {
                Directory.CreateDirectory(FileName.Substring(0, FileName.LastIndexOf("\\")));
            }
            line = $"{DateTime.Now:HH:mm:SS:fff}=>{line}";
            writeline(FileName, line);
            }
        public object obj = new object();
        private void writeline(string fileName, string line)
        {
            lock (obj)
            {
                LogInfo logInfo = new LogInfo();
                logInfo.path = fileName;
                logInfo.line = line;
                logQueue.Enqueue(logInfo);
            }
          
        }
    }
   
}

2.LogServer程序讲解

1.队列类型(ConcurrentQueue<T>)

首先C#的队列的建立是通过ConcurrentQueue<T>进行创建,首先什么是队列,其实对列很简单,就是FIFO先进先出。我们可以看下labview实现队列的方式

首先labview队列完整实现需要的函数是获取队列-入队列-出队列-结束队列

首先看下Labview的队列引用 ,引用中包含队列名,元素的数据类型,这个是队列最重要两个参数

   

我们看一下C#程序中队列是怎么引用的,首先是ConcurrentQueue(线程安全队列),他的本质是什么,C#有描述,因为它后面带了T,所以肯定是泛型而且是一个特殊的泛型集合。

C#还有一个Queue(非安全型队列)这个在多线程引用会出现崩溃的问题,多次Thread,会有问题。程序解读:就是实例化一个名称为logQueue的一个队列,队列元素是LogInfo(类),等同于Labview的获取队列引用

1.2队列的基本格式
private ConcurrentQueue<你要存的类型> 队列名称 = new ConcurrentQueue<你要存的类型>();

2.单例模式(instance)

我们为什么建立队列后采用单例模式,其实与Labview的理念是相同的,在labview使用队列的时候,普遍都是在多个VI进行调用队列,往同一个队列状态机里传递队列元素 ,不可能是多个VI调用队列 ,往多个相同队列状态机里传递,那样传递,程序逻辑可以不用玩了。所以C#在使用队列的时候其实也是这样的一个设计思路,所以C#在使用队列的时候,必须创建一个单例模式,禁止外部创建新对象 ,保证程序里只有LogServer一个对象,也就永远只有一个 logQueue 队列,所有调用的都往这一个队列里塞。

分析LogServer ,其实跟Labview的解释是一样的,我们先看下我们正常编写Labview的时候,我们也是一直通过While循环,等待出队列,等待时间为100,也就是意味着每隔100毫秒检查队列元素,并输出。

C#中创建了一个task,Task.Run((()=>doWork())这个意思是从异步调用线程池中取出一个线程去执行doWork方法并赋值给task这个变量实例,它的作用是在不影响主线程的情况下,异步执行一个线程,相当于我们Labview创建了一个需要执行队列状态机的VI,VI名称时doWork,并且我们触发这个Vi是通过异步调用实现的。

我们讲一下C#中Whie循环中的意义,就是当我们把线程分配给doWork后,进入While循环,循环结束条件设置为true,在While循环中判断如果doWork的状态时执行了,那么通过Break实现Whie循环结束。

它这个的作用相当与Labview的对VI在调用时,去判断调用vi时的一个状态,如果doWork已经运行了,那么就退出While,反之,等待200ms继续检查线程有没有执行。

2.1doWork

在DoWork方法中,While循环,在循环中,我们通过if判断,判断.logQueue.Count(队列元素个数)如果大于0,将元素取出并Out输出参数logInfo。logQueue.tryDequeue等同于出队列。

2.2SteamWriter

StreamWriter 是 C# 里专门用来写文本文件的类,会自动处理编码、换行等细节,与labview的文本写入相类似,SteamWriter跟labview一样,也是需要一些设置参数,

logInfo.path=文件路径

true=代表内容是不是覆盖还是追加模式写入下一行;与labview写入带分隔符电子字符表格函数中添加至文件?的设置是相同的

Encding.UTF8,=文本格式,这个格式跟labview是不一样的代表的是文本格式,是ASCII,还是其他因为文本内容有中文所以这里用的是UTF8,如果只有英文就可以用ASCII,labview的格式是输入参数的类型,二者不一样

然后WriteLine写入内容

2.3.Try..catch

C#的Try..catch与labview对错误簇的处理机制是相同的,我们读一下Try..catch C#的代码内容先执行文本写入,如果文本写入不成功,那么进入catch,先判断一下写入失败次数是不是小于20,如果小于20,那么等待100毫秒,再尝试执行文本写入 ,如果已经写入失败次数超过20,那么就结束。

2.4.单例模式格式
public class 类名
{
    // 1. 唯一的静态实例(全局只有这一个对象)
    public static 类名 Instance { get; } = new 类名();

    // 2. 私有构造(禁止外部 new 对象)
    private 类名()
    {
    }
2.5WriteLog方法讲解

建立绝对路径D:\log\                 

创建文本文件夹名称,文本文件名称

这个相当于Labview检查文件是否存在,如果文件不存在,创建文件       

Directory.Exists作用就是检查是否存在,与labview检查文件/文件夹是否存在一致

Directory.CreateDurector作用就是创建目录,跟labview的创建文件夹及文件函数一致

Substring有点相当于匹配模式,因为这个labview的字符串的函数其实我感觉用起来,有好几个函数功能上带有点多功能的效果,C#字符串语句其实分的比较细,在语法基础使用上C#和labview还有非常大的区别。在程序中创建文本的条件就是检查是否搜索匹配到路径"\\",如果没有,就创建

将写入数据传递到writeline方法里        

2.6 writeline方法

writeline的作用其实就是将路径和内容传递给logInfo,首先将LogInfo实例化,然后将获取的参数进行赋值,将含有path和line参数的logInfo进入队列,我们之前代码出队列变量是logInfo,所以入队列的时候变量也是logInfo,然后在出队列

2.7logQueue.Enqueue和Lock

入队列,这个就是Labview的入队列,将logInfo入队列,但是C#的入队列跟labview的入队列有很大的不同,labview入队列的时候,不管多少线程同时往里扔数据,它自己内部会排队、上锁、保护,C#没有这个机制,如果很多地方都在调用,队列内部结构会被破坏,数据乱掉甚至崩溃,所以它要加一个lock,上锁机制,lock 的作用 = 排队执行,同一时间只允许一个人操作队列。

2.8Lock基础格式

1.定义一个锁对象,就是相当家里要挂一个锁头,啥牌子的呢,就是logLock这个牌子的锁

// 定义一个锁对象(锁队列用)
private readonly object logLock = new object();

2.上锁格式,lock 的作用 = 排队执行,同一时间只允许一个人操作队列。

// 上锁 → 同一时间只能有一个线程执行
lock (logLock)
{
    logQueue.Enqueue(logInfo);
}

3.LogInfo程序讲解

编写这个程序类的目的是为了WPF前面板Btn触发后,有方法将路径和内容传递给LogServer中的writeLog

4.程序运行逻辑

前面板btn按下后,触发LogServer单例,并将写入内容,通过writelog方法传递进去

立马进入方法writelog,然后开始进行文本检查,创建文本,然后入队列

出队列,写入文本

总结

其实我们在编写程序的时候不管用什么语言,逻辑思路其实大体都是一样的,labview实现文本的写入也是这套逻辑,只不过展现方式不一样。语言只是代码逻辑表达展示的一个方式,没有谁优谁差。labview想对高度集成了一些,就像乐高积木一样,模块化的比较多,C#更像一个碎片,更锻炼程序员的一个思维能了,需要把碎片语言粘合成一个方法,在组成类,然后再拿去使用。所以C#的学习周期反而更长,对程序员的语言基础要求非常高。语言没有好与坏,学精了都不易。

我做了8年的labview,用labview也曾独立带过大型项目,直到现在我也不敢说我把labview用的有多精,什么问题都能解决。学东西越往深了学,越会发现,自己其实还有好多不明白的地方,就像数学一样,越往深了学,越会发现自己好多东西都学不明白,甚至已经超出自己能理解的思维范畴了。语言也是一样,我只能说,够用,能应付现在的工作,但绝不敢说自己有多牛。

学无止境,与君共勉

         

更多推荐