대부분의 어플리케이션에서, 우선순위 레벨 또는 자동적으로 VI의 멀티태스킹을 처리하는 표준 실행 시스템 외의 실행 시스템을 사용할 필요가 없습니다. 기본적으로 모든 VI는 표준 우선순위일 경우 표준 실행 시스템에서 실행됩니다. 멀티스레드 어플리케이션에서, 별도의 스레드가 사용자 인터페이스 작업을 처리하여 VI가 사용자 인터페이스 상호작용으로부터 차단되도록 합니다. 답일 스레드 어플리케이션의 경우에도, 실행 시스템은 사용자 인터페이스 상호작용과 VI의 실행을 번갈아 처리하므로 비슷한 결과가 나옵니다.

일반적으로, 실행의 우선순위를 정하는 가장 좋은 방법은 어플리케이션에서 기다림 함수를 사용하여 낮은 우선순위 루프의 속도를 줄이는 것입니다. 100에서 200ms의 지연은 거의 사용자의 눈에 띄지 않으므로 이는 특히 사용자 인터페이스 VI를 위한 루프에 유용합니다.

우선순위를 사용하는 경우 신중하게 사용하십시오. 상당 기간동안 동작하는 상위 우선순위 VI를 설계하는 경우, 이 코드의 비교적 시간에 결정적이지 않은 섹션의 VI에 기다림을 추가하여 하위 우선순위 작업과 시간을 공유할 수 있도록 고려하십시오.

다른 작업이 변경할 수 있는 글로벌 변수, 로컬 변수, 또는 다른 외부 리소스를 사용할 때는 주의하십시오. 기능적인 글로벌 변수 또는 세마포어와 같은 동기화 기술을 사용하여 이 리소스에 대한 접근을 보호할 수 있습니다.

글로벌 및 로컬 변수와 외부 리소스 접근 동기화하기

실행 시스템이 여러 작업을 병렬로 실행할 수 있으므로, 글로벌과 로컬 변수 및 리소스가 올바른 순서로 접근되도록 해야합니다.

경합 조건 방지하기

다음 여러가지 방법 중 하나로 경합 조건을 피할 수 있습니다. 가장 간단한 방법은 글로벌 변수가 전체 어플리케이션에서 변경되는데 이 글로벌 변수를 단지 한 곳에서만 가지는 것입니다.

단일 스레드 어플리케이션에서, 서브루틴 우선순위 VI를 사용하여 경합 조건을 발생시키지 않고 글로벌 변수를 읽거나 쓸 수 있습니다. 서브루틴 우선순위 VI는 다른 VI와 실행 스레드를 공유하지 않기 때문입니다. 멀티스레드 어플리케이션에서, 서브루틴 우선순위 레벨은 글로벌 변수로의 독점적인 접근을 보장하지 않습니다. 다른 스레드에서 실행되는 다른 VI가 동시에 글로벌 변수에 접근할 수 있기 때문입니다.

기능적인 글로벌 변수

기능적인 글로벌 변수는 초기화되지 않은 시프트 레지스터가 있는 루프를 사용하여 글로벌 데이터를 얻는 재호출이 아닌 VI입니다. 기능적인 글로벌 변수는 일반적으로 VI가 어떤 작업을 수행할지 지정하는 동작 입력 파라미터를 가지고 있습니다. 기능적인 글로벌 변수를 사용하여 변수에서 작업에 접근하는 코드의 중요 섹션을 보호할 수 있으며, 글로벌 변수와 관련된 경합 조건을 방지할 수 있습니다. 글로벌 변수를 사용하면 변수의 값을 읽고 변경한 후 다시 값을 쓸 때 경합 조건이 발생할 수 있습니다. 이는 동시에 하나의 변수 값을 읽은 병렬 코드의 서로 다른 두 부분이 다른 부분에 도입된 변경 사항을 덮어쓸 수 있기 때문입니다. 기능적인 글로벌 변수를 사용하면 데이터를 변경하는 작업을 보호할 수 있습니다. 예를 들어, 기능적인 글로별 변수는 읽기, 증가 및 메모리에 데이터 쓰기 작업 또는 데이터베이스 업데이트 또는 파일 변경 작업과 같은 작업을 포함하는 중요 섹션에 대한 경합 조건을 방지할 수 있습니다.

다음 그림은 단순한 카운트 글로벌 변수를 실행하는 기능적인 글로벌 변수를 보여줍니다. VI는 While 루프의 초기화되지 않은 시프트 레지스터를 사용하여 작업의 결과를 유지합니다. 이 예제의 동작은 초기화, 읽기, 증가, 감소입니다.

VI를 호출할 때마다 루프의 블록다이어그램은 정확하게 한 번 실행됩니다. 동작 파라미터에 따라, 루프 안의 케이스는 시프트 레지스터의 값을 초기화하거나, 변경하지 않거나, 증가시키거나, 감소시킵니다.

위의 예제와 같이 기능적인 글로벌 변수를 사용하여 단순한 글로벌 변수를 구현할 수 있지만, 기능적인 글로벌 변수는 스택 또는 큐 버퍼와 같은 더 복잡한 데이터 구조를 구현할 때 특히 유용합니다. 또한 기능적인 글로벌 변수에 대한 호출은 VI를 재호출로 표시해놓지 않는 이상 순차적으로 실행되기 때문에, 기능적인 글로벌 변수를 사용하여 파일이나 인스트루먼트, 그리고 데이터 측정 디바이스와 같은 글로벌 리소스에 대한 접근을 보호할 수 있습니다.

기능적인 글로벌 변수로 대부분의 동기화 문제를 해결할 수 있습니다. 한 번에 하나의 호출만 발생시키는 기능적인 글로벌 VI가 포함된 데이터를 변경하기 때문입니다. 기능적인 글로벌 변수의 한 가지 단점은 변수가 가진 리소스를 수정하는 방식을 변경하려할 때, 기능적인 글로벌 VI 블록다이어그램을 변경하고 새 작업을 추가해야 한다는 것입니다. 글로벌 리소스가 자주 변경되는 몇몇 어플리케이션에서, 이러한 변경은 불편할 수 있습니다. 그런 경우에, 세마포어를 사용하도록 어플리케이션을 디자인하여 글로벌 리소스로의 접근을 보호합니다.

데이터 통신 방법

세마포어

접근 해제(Mutex)로도 부르는 세마포어는 공유 변수와 같이 공유된 리소스로의 접근을 보호하는데 사용할 수 있는 객체입니다. 공유된 리소스에 접근하는 코드는 중요한 섹션이라고 부릅니다. 동시에 세마포어에 접근할 수 있도록 허용하는 태스크의 숫자를 제한함으로써, 세마포어는 중요한 섹션에 대한 접근을 보호합니다. 일반적으로, 한 번에 하나의 작업만이 공동 세마포어로 보호되는 중요한 섹션에 접근하도록 합니다. 그러나, 하나 이상(최대 미리 정의된 리미트까지)의 태스크가 중요한 섹션에 접근할 수 있도록 세마포어를 설정할 수도 있습니다.

세마포어를 사용하는 대부분의 경우에는 다음 단계를 따라야 합니다:

  1. 코드의 중요한 섹션 찾아내기—일반적으로 세마포어를 사용하여 공유된 리소스에 접근하는 코드의 섹션에 접근합니다. 예를 들어, 여러 SubVI가 한 개의 공유 변수와 상호 작용하는 경우, 이러한 코드의 섹션이 동시에 실행되지 않도록하여 경합 조건이 발생하는 것을 방지합니다. 이 예제에서 글로벌 변수로부터 읽거나, 변수에 쓰는 코드의 각 섹션은 코드의 중요한 섹션입니다. 파일 및 디바이스 I/O는 중요한 섹션을 구성하는 코드의 대표적인 예입니다.
  2. 중요한 섹션이 공유하도록 세마포어 생성[세마포어 참조 얻기] VI를 사용하여 새 세마포어를 생성하거나 기존에 존재하는 세마포어를 이름으로 찾아 불러옵니다. 새로 생성한 세마포어는 한 개의 태스크에 의해서만 사용되도록 기본 설정되어 있습니다. 여러 태스크가 이 세마포어에 동시에 접근할 수 있도록 하려면, [세마포어 참조 얻기] VI의 크기 입력에 태스크의 수를 지정합니다.
  3. 각 중요한 섹션 전에 세마포어에 접근[세마포어 얻기] VI를 사용하여 세마포어에 대한 접근을 요청합니다. 다른 태스크가 세마포어를 이미 사용하고 있는 경우, 데이터 흐름은 이 태스크가 세마포어를 더 이상 사용하지 않을 때까지 [세마포어 얻기] VI에 멈추어 있습니다. 코드의 한 섹션이 세마포어를 수집하면, 다른 태스크는 이 코드가 세마포어를 해제할 때까지 세마포어를 수집할 수 없습니다. 그러므로, [세마포어 얻기] VI와 [세마포어 해제] VI 사이에서 보호되어 있는 코드는 항상 다른 보호된 코드 섹션이 실행을 시작하기 전에 실행을 완료하도록 보장됩니다.
  4. 각 중요한 섹션 실행 후 세마포어에 대한 접근 해제[세마포어 해제] VI를 사용해서 세마포어에 대한 접근을 해제하여 대기 중인 다른 태스크가 이 세마포어를 수집하고 코드의 중요한 섹션을 실행하도록 합니다.
  5. 세마포어에 대한 참조 해제[세마포어 참조 해제] VI를 사용하여 세마포어에 대한 참조를 해제합니다. 시스템 리소스를 효율적으로 사용하려면 이 세마포어를 사용하는 모든 태스크가 완료된 것을 확인한 후에 세마포어 참조를 해제합니다.
노트 더 숙련된 방법으로 세마포어를 사용하면 세마포어의 수집과 해제를 코드의 같은 섹션에서 하지 않아도 됩니다.

[세마포어 참조 얻기] VI와 [세마포어 참조 해제] VI를 사용하는 예제는 labview\examples\Synchronization\Semaphore 디렉토리에서 Simple Semaphore VI를 참조하십시오.

다음 그림은 세마포어를 사용하여 중요한 섹션을 보호하는 방법을 보여줍니다. 다른 VI가 세마포어를 생성하여 참조를 이 SubVI로 전달했습니다. 이 세마포어의 크기는 1이므로, 한 번에 한 개의 태스크만 세마포어에 접근할 수 있습니다.

이전의 블록다이어그램은 각각 같은 공유 변수인 카운트에 접근하는 중요한 섹션을 포함합니다. 그러므로, 이러한 블록다이어그램은 같은 세마포어를 공유합니다. 각 블록다이어그램은 중요한 섹션을 실행하기 전에, [세마포어 얻기] VI를 호출하여 다른 블록다이어그램이 이 세마포어를 이미 수집했는지 감지합니다. 세마포어를 사용 중인 경우, [세마포어 얻기] VI는 세마포어가 사용 가능할 때까지 기다립니다. 세마포어가 사용 가능하게 되면, [세마포어 수집] VI는 타임 아웃에 거짓을 반환하여 블록다이어그램이 중요한 섹션을 실행할 권한이 있음을 나타냅니다. 블록다이어그램이 중요한 섹션의 실행을 종료할 때 [세마포어 해제] VI는 세마포어를 해제하여, 대기 중인 다른 블록다이어그램이 실행을 재개할 수 있도록 허용합니다.

세마포어 지속기간

유휴 상태가 아닌 최상위 VI가 세마포어에 대한 참조를 소유하고 있는 한 세마포어는 메모리에 남아있습니다. 최상위 VI가 유휴 상태가 되면, LabVIEW는 최상위 VI의 SubVI 안에 존재하는 참조를 포함하여 해당 VI가 소유하는 모든 세마포어 참조를 해제합니다. LabVIEW가 이름있는 세마포어에 대한 마지막 참조를 해제하면, LabVIEW는 세마포어를 제거합니다. 이름없는 세마포어에 대한 참조는 오직 한 개만을 가질 수 있으므로, LabVIEW는 최상위 VI가 유휴 상태가 될 때 이름없는 세마포어를 제거합니다. 여러 최상위 VI에서 세마포어를 사용하려면, 세마포어에 이름을 붙이고 각 최상위 VI에서 [세마포어 참조 얻기] VI를 호출하여 각 VI가 자체적으로 세마포어에 대한 고유의 참조를 얻을 수 있도록 합니다.