我用C#.NET开发的软件与OMRON的PLC连接,PLC上挂OMRON的触摸屏,
数据库方面使用SQL Server数据库,使用的是存储过程,存储过程稍微有点复杂
C#软件使用一个Timer控件实现同步监控PLC内存数据,读取或者改写PLC的内存,并实现软件界面数据的更新。
但是最终软件的运行速度不理想,软件运行界面会出现几秒的延迟,同时软件读取单个触摸屏上的数据也有相应的延迟。
请问哪位高手能不能帮小弟想下法子,看能不能使软件在运行速度上有所提升。另外,小弟稍微研究了下多线程,但是对于多线程使用的位置和方法不是很明确,有哪位高手能不能指点下。最后提一下,运行软件的服务器是4核CPU,内存1G。迫切期待问题的解决,先谢谢各位了!

解决方案 »

  1.   

    触摸屏只是PLC连接的一个终端设备,C#软件不需要直接对触摸屏进行数据读写,而是对PLC内存进行数据读写
      

  2.   

    timer同步??timer是定时执行的,从你的需求来看是要事件触发的,就是触摸屏只要有操作,马上就要把数据更新到你的服务器,通过timer去读数据??
    呵呵,哪怕你的timer间隔时间定在500毫秒,也会有500毫秒的延迟。
      

  3.   

    如果能像你讲的那样当然好了,但是现在做不到触摸屏触发,实际上是没有异步监控的方法,只能同步监控,
    而且现在问题是延迟的时间并不是Timer控件的时间间隔那么短,而是达到了几秒的长度
      

  4.   

    你现在是异步监控,同步监控是即时触发的,你是通过timer来监控数据的,每隔一段时间去遍历plc里的内存,而timer事实上是定时启动线程去做的。首先你要确定真正慢的地方在哪里,是存储过程执行慢,还是去读取plc的内存时慢。还是即使更新了plc内存,plc把数据更新到触摸屏本身就很慢,确定问题所在后再来解决,你这样问别人,别人也不知道具体是什么原因导致的。
      

  5.   


    多谢楼上的指点。其实分开来讲,楼上讲的这几点都不慢,但是,由于触摸屏块数比较多,而且每块触摸屏操作完毕都有相应的数据处理要求,
    所以整个系统整体来看,就变慢了。我想过用多线程来处理,每个线程单独处理一块屏的数据,但是对于线程分开与PLC进行数据交互时,会不会有数据紊乱的现象发生这是个未知之数,不知道楼上的高手对这方面有什么见解。再次谢谢高手指教!
      

  6.   


    谢谢,现在总共有15块触摸屏,但是数据总合在一个PLC中,如果多线程在四核CPU的电脑中运行是同时的(微观上的)(这点我现在还不能确信),那么就有可能同时访问PLC,那么PLC响应的数据就会紊乱。系统的存储过程数量也不少,总共程序也不少,因为数据要进行的分析和计算、控制比较多,所以就慢了。
    当然,既然软件运行比较慢,那么肯定是有值得优化的地方。再次谢谢!
      

  7.   

    plc内存里总归能区分15块触摸屏的关键字吧,不然你也无法知道是哪块的数据了,既然有这个关键字,通过这个关键字去访问各自的内存,怎么会数据乱呢?如果你怕多线程的情况会导致对plc的内存造成混乱,那么对于plc的访问你可以加上锁,每次只允许一个线程去访问,因为从你描述的情况来看,就plc处共享了数据,其他地方似乎可以各自干各自的,而影响性能的地方似乎也是在对拿到的数据后的计算以及访问数据库上,那么你对于plc的访问加上锁就 可以了。
      

  8.   

    多线程操作窗体控件方法:
    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。再举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个TextBox.. 
    using System.Threading;       public delegate void MyInvoke(string str);
            private void btnStartThread_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(new ThreadStart(DoWord));
                thread.Start();
            }
            public void DoWord()
            {            
                MyInvoke mi = new MyInvoke(SetTxt);
                BeginInvoke(mi,new object[]{"abc"});                
            }        public void SetTxt(string str)
            {
                txtReceive.Text += str + System.Environment.NewLine;
            }多线程详细请看:.Net中的Thread类:http://hi.baidu.com/grrc/blog/item/6c257beea88768fdb3fb95c7.html
      

  9.   


    谢谢!听起来好像是这么回事,但是我对多线程在这种情况下的应用还不怎么知道,大虾能不能教小弟以下关于对PLC访问的上锁操作和多线程的应用方法,如果可以的话,最好有个简单的例子,或者发个链接给在下,让我学习学习,以解决燃眉之急!另外,请问一下,照现在这样的分析,如果使用了多线程,而且运行软件的电脑又是4核CPU的,那么是不是可以解决速度问题,让线程在微观上也是同时监控不同的点,从而达到监控不同的屏而不出现串行执行的现象?谢谢了
      

  10.   

    public static  class PLCAccessHelper
    {
    readonly static object _lokcer = new object();
    public static PLCDataClass Get(int sectionID)
    {
    lock(_locker)
    {
     return InvokePLCApI.Invoke();
    }
    }public static void Put(int sectionID,PLCDataClass data)
    {
    lock(_locker)
    {
    InvokePLCApI.Invoke(...);
    }
    }
    }
    Thread[] tds = new Thread[Environment.ProcessorCount * 2];
    for(int i = 0;i < tds.Length; i ++)
    {
    tds[i] = new Thread(delegate(){
    PLCAccessHelper.Get();
    ...
    PLCAccessHelperPut();
    }
    );
    tds[i].Startz();
    }代码大致是这样,应该能看懂是怎么回事吧
      

  11.   

    如果你的CPU是AMD那就比较费劲了。在执行这种托管程序方面AMD实在很难与Intel相比.
      

  12.   


    谢谢回复!好像知道怎么回事了,但是有的地方又不是很明白,能不能加点注释啊,特别是这一段:Thread[] tds = new Thread[Environment.ProcessorCount * 2]; 
    for(int i = 0;i < tds.Length; i ++) 

    tds[i] = new Thread(delegate(){ 
    PLCAccessHelper.Get(); 
    ... 
    PLCAccessHelperPut(); 

    ); 
    tds[i].Startz(); 
    } 好人做到底嘛,谢谢了!
      

  13.   

    Thread[] tds = new Thread[Environment.ProcessorCount * 2]; 
    for(int i = 0;i < tds.Length; i ++) 

    tds[i] = new Thread(delegate(){ 
    PLCAccessHelper.Get(); 
    ... 
    PLCAccessHelperPut(); 

    ); 
    tds[i].Startz(); 
    } 这里把委托delegate放在线程里,然后调用
    PLCAccessHelper.Get(); 
    ... 
    PLCAccessHelperPut(); 谢谢!
      

  14.   

    Thread[] tds = new Thread[Environment.ProcessorCount * 2]; 
    for(int i = 0;i < tds.Length; i ++) 

    tds[i] = new Thread(delegate(){ 
    PLCAccessHelper.Get(); 
    ... 
    PLCAccessHelperPut(); 

    ); 
    tds[i].Startz(); 
    } 这里把委托delegate放在线程里,然后调用 
    PLCAccessHelper.Get(); 
    ... 
    PLCAccessHelperPut(); 解释下,谢谢!
      

  15.   

    这是c#语法问题,普通的写法是另外写一个函数,然后把这个函数传递给线程,这是c#3.0的最新写法,这样的好处就是线程里可以直接使用启动线程的函数的变量。编写程序会比较方便。你不是希望多线程来处理15个触摸屏块的数据吗?我是假设启动的线程数是cpu数*2,但你完全可以启动15个线程的,我代码的意思是自己定义了一个访问PLC的静态类,这个类会对PLC的访问加上锁,这样线程使用这个静态类去访问PLC的时候就回总是只有一个线程在访问其他线程会被锁拦在外面了,等访问的线程释放锁后就是执行完lock里的代码后,另外等待的某个线程就又进入lock里的代码执行了。省略号的地方就是你拿到PLC内存数据后你要做的事情,计算啦,调用存储过程什么的,这样就是并发对15个块来处理了,调用完了又把数据写入到PLC,也是加上了锁的。至于你起了15个线程是否真的并发什么的,你不用去关心,操作系统会替你分配的。可以直接指定线程挂到某个cpu上,但没有这个必要,而且你不知道除了这15个线程之外其他的是否也要使用cpu否,所以让操作系统自己去分配,这样不会影响到其他工作。希望说清楚了
      

  16.   


    已经很清楚了,谢谢大虾进行回复分析!此帖先到这结帖!谢谢各位,再次谢谢tmxk2002
      

  17.   

    是不是可以不用timer来检查有没有触摸屏数据,应该在触摸屏有数据时就启动相应线程啊