cpubbs论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

LabVIEW+单片机学习套件全套教程资料下载[免费]LabVIEW论坛精华列表贴USB0816数据采集卡《LabVIEW宝典》
LabWindows/CVI论坛精华贴NET0816以太网数据采集卡RC0210远程设备授权系统 关闭关停锁定打开设备 户外分布式数据采集
NET1624低速高精度以太网数据采集卡WIFI0824SD无线WIFI网络数据采集卡脱机运行 SD存储 小尺寸微型 串口采集远程采集 安卓 手持移动采集 纪录仪
查看: 1877|回复: 3

再讨论全局变量

[复制链接]
发表于 2008-7-16 15:52:58 | 显示全部楼层 |阅读模式
因为有一个应用要用到一个大的结构体数组,而且希望要设为全局变量,一直以来对于全局变量心存疑惑,不知道lv编译器怎么看待这个全局变量,是类似于c中的静态变量,只有一份,还是会拷贝一份。来到论坛查了一下,
http://www.cpubbs.com/bbs/viewth ... B%BE%D6%B1%E4%C1%BF
中有讨论,认为是内存中同一份,为了确认,到NI网站也搜索了一下,查到如下资料。

Are LabVIEW global variables good or bad, and when is it OK to use them?

When someone categorically states that globals are bad, they are of course oversimplifying the explanation, probably so that you will leave them alone so they can get some work done. Similarly, the statement that globals are great, otherwise NI wouldn't have them in LabVIEW, fails to tell the whole story. The two common problems that globals will cause in an application are race conditions and performance problems. These problems are pretty easy to diagnose and relatively easy to fix once you understand them.
Race Conditions

A race condition occurs when more than one piece of code needs to update a global and there is no way of ensuring that they take turns and leave the global in a valid state. As an example, let's look at the case when a global string is used to log important messages for the user of your VI. To add to the log, you read the global string, append your text, and write the updated string back to the global. There is no problem with this usage until you copy the code and add it to something that may run in parallel with the original.

Since the problem this causes may not be obvious, the race condition occurs when two pieces of code want to log a message at roughly the same time. The first piece of code reads the log -- copying it to its diagram wire. Then the second code reads the log string to its wire. Now both pieces of code append their message to the original log string. The problem is that neither of the log strings contains both messages. The two pieces of code will now write to the global, one at a time, and only one of the two messages will have been added. The other lost the race. As you might imagine, this bug can be in the application a long time, and you will only occasionally lose a message, so they are often some of the sneakiest bugs to track down.

The solution is to control the access, making the code take turns so that the log is written by the first before the second reads it. There are multiple ways of doing this, but the easiest is to build a subVI. The message to append is a parameter, and the subVI diagram does the read-modify-write. It is important that you do not change the VI to be reentrant or you have reintroduced the race condition, and it is important that the VI is used throughout the application, as this VI running in parallel with other code directly updating the global still has a race condition. In fact, another improvement is to change the subVI to use a shift register or a local variable to store the log message and remove the global variable entirely. With this change, all code must call the subVI to update the log since they have no other way of accessing it.

Performance Problems

It is also possible to use globals in ways that greatly slow down your VI's execution. This is very different from a race condition in that the program executes correctly, but it takes longer than it should to complete. For an example, let's look at an array of constants needed throughout your application. You read the data from a file, a database, or wherever, and write it to the global early in your VI. Numerous parts of your application read the global data and index to get the needed constant. As long as the table of constants is relatively small, maybe around 1,000 elements, this approach works fine. But then you load up 1,000,000 elements ... and your program slows to a crawl.

It may not be obvious, but each read of a global copies the data, places it on the wire, and sends it to the next node. It has no parameters telling it which values you are interested in, so it reads the entire global. When this code is placed in a loop, it has no way of knowing if the global data is the same as last read or if other code has modified it, so it reads the entire global each and every time the global terminal executes. You may be thinking -- "That's dumb. My code is obviously only interested in one or two values, so why doesn't the LabVIEW compiler do something smarter?" Good question, but sort of a complicated one as well.

The quick answer is that the LabVIEW compiler has to be pretty defensive -- globals being globally accessible, and LabVIEW being a parallel language. At any point in time, another VI could come into memory that writes to the global. If a VI were compiled in a special way to try and lock out writers while your indexing, searching, or other downstream code runs, it could easily cause a deadlock in your application. In case deadlock is a term you aren't familiar with, it means that your application is hung and will never finish -- a VERY BAD thing. More precisely, this happens when two pieces of code need to lock two globals before running. Each piece of code will lock one of the globals, then wait forever for the other piece of code to release the global it needs. So this is one of those slippery slopes where it seems so easy for the LabVIEW compiler to help out and magically make this more efficient, but it opens the door to a much bigger problem -- deadlocks. Fortunately, there is a very good solution that will actually make the code easier to read at the same time it restores the performance.

To speed this up, make a subVI that takes the parameters for indexing the global and returns the data. The subVI diagram reads the global, indexes and returns only the data you asked for. This avoids putting lots of data on wires all over your application and will reduce the size of your application at runtime, but it can be further improved. Since the data is still in a global location, LabVIEW still has to be worried about other pieces of code changing it, so the subVI is still reading the entire global onto its wire each time it runs. By moving the data storage to be private to the subVI, this need is removed. Specifically, if the subVI has a loop with an unwired shift register, this data cannot be updated by anything but the right hand shift register terminal, and can only be read from the left hand terminal. This limited access allows LabVIEW to avoid the copy of the data, and the index can now work directly on the data and return only the element you are interested in. After adding the subVI and shift register, it takes less time to read from the 1,000,000 element global than it took to read from the 1,000 element global the initial way.

The Solution

You've probably already noticed that the solution to both of these problems is pretty much the same. You can even add to this technique to return a particular row, column, statistical data, or any other function of the global data and the inputs to the VI. To do this, add an enum or ring selector for the operation to perform, along with inputs and outputs needed by each additional operation. If many operations are added, it may be a good idea to add a wrapper VI for each operation with just the inputs and outputs needed, and with a constant for the operation. This also allows you to build a good name, description, and icon for the operation on the global. For an example of a more capable global, look at <labview directory>/examples/general/globals.llb/Smart Buffer Example.vi

The Summary:

    * Simple datatypes with simple access patterns work great with normal globals.
    * Complex access patterns can cause race conditions.
    * Complex datatypes or large arrays may cause performance problems.
    * Functional (LabVIEW 2 Style) globals can be used to solve both of these problems

从资料看来,全局变量在使用的时候是有了一个 内存copy,是在使用的时刻从全局变量copy的,这样可以解释为什么我们使用全局变量可以得到变化的值,但往全局变量中写的时候的操作:是不是把内存中最初的那个变量给刷新,在这个资料中看不出来。
所以使用大数组时尽量要避免使用全局或局部变量,在同一个线程中最好是采用移位寄存的方式,如果需要在两个线程之间使用的话,如资料中所说,尽量传送关心胡变量,如数组中要用到的一个元素或子数组。
希望大家讨论,我也理解还不是很透彻。
 楼主| 发表于 2008-7-16 16:24:07 | 显示全部楼层
我自己写了一个简单的vi测试了一下,使用的时候的确是copy了一份,可以见我的vi,初使化了一个64M的数组,运行时打开任务管理器监控内存的使用可以发现,运行起来之后内存使用了64M,点击测试时使用了全局变量,内存又使用了64M,但最后把改变的值写回全局变量时内存没有增加,我觉得验证了我的估计,不知道大家有没有不同的看法。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2008-7-19 21:31:02 | 显示全部楼层
我不太懂这个东西,没用过,希望高手们讨论出个结果,我也好学习一下
发表于 2008-7-19 21:51:41 | 显示全部楼层

尽量不用全局变量

最多只用功能性全局变量,可去ruanqizhen.space.com看下,有详细中文解释。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|cpubbs论坛. ( 粤ICP备09171248号 )

GMT+8, 2024-5-19 03:24 , Processed in 0.465061 second(s), 8 queries , Gzip On, File On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表