심도있게 들어가면 꽤 까다롭네..
몰랐던 사실중에 중요한 내용은 Console 과 WPF 에서 async/await 의 thread 수행 형태가 다르다!
async/await에 대한 “There Is No Thread” 글의 부가 설명
심도있게 들어가면 꽤 까다롭네..
몰랐던 사실중에 중요한 내용은 Console 과 WPF 에서 async/await 의 thread 수행 형태가 다르다!
async/await에 대한 “There Is No Thread” 글의 부가 설명
Use : J-Link / J-TRACE Cortex를 선택
Port : SWD 로 변경
Max Clock : 50Mhz로 변경 ( 이건 좀 확인해봐야 할듯..)
위 그림처럼 설정한다
확인 누르고 나온다
Use External Tool for Flash Programming 선택
Run Independent 체크
concurrently(동시성), parallelism(병렬화), asynchronous(비동기)
이름부터 어렵다
비슷한것 같으면서도 다르기 때문에 혼란을 야기하는데, 여기에 정리해놓고 리마인드 해야지
다만, 이 내용이 정확한 내용인지는 나도 장담 못함…
여러 작업이 번갈아 가면서 동시에 실행되는 것을 말한다
동시라고는 하지만 실제로는 Context Switching에 가깝다
따라서 Single core에서도 동시성이 있는 프로그래밍을 하고 수행할 수 있다.
thread 기반 프로그래밍이 동시성 프로그래밍의 좋은 예이다.
single core에서도 다수의 thread를 만들어서 수행할 수 있으며, 사용자가 보기에 동시에 실행되는 것처럼 빠르게 번갈아 가면서 수행된다
concurrently 프로그래밍은 태생적으로 resource에 대한 race condition이 발생 한다.
예를 들어서 전역 변수를 접근하는 concurrently 프로그램을 짠다면 하이젠버그를 만나게 될 것이다.
여러 작업이 다수의 core에서 동시에 수행되는 것을 말한다.
병렬화 프로그래밍은 single core에서는 불가능하다. 반드시 다수의 코어가 실제적으로도 동시에 일을 해야 하는 것을 말한다.
이런 의미에서 보면 동시성 프로그래밍을 한 후에, multicore에서 실행하면
“야호~ 나 프로그램을 병렬화 했어” 라고 말할 수 있을까?
OS가 알아서 작업을 각각의 core에 알아서 스케쥴일 해줄테니 말이다.
잘 모르겠지만, 아닌것 같다.
병렬화는 프로그래머가 명시적으로 해당 부분을 강제로 다수의 코어가 나누어서 수행한다는 의미가 강하다.
예를 들어서 1에서 100까지 더해서 합하는 프로그래밍을 한다고 해보자.
이때 병렬화 프로그래밍을 한다는 것은, 1~50까지는 1번 코어가 계산하도록 하고, 51~100까지는 2번 코어가 계산을 하도록 한후에 나중에 합치는 프로그래밍을 할 것이다.
보통 동시성 프로그래밍을 한다고 하면, 위 작업같은 조치를 취하지 않는다.
병렬화 프로그래밍은 대부분이 프로세서가 시간이 오래 걸리는 연산 작업을 다수의 코어가 나눠서 같이 계산을 한다는 의미로 많이 쓰인다.
이런 용도로 병렬화 라이브러리가 OpenMP이다. OpenMP를 쓰는 프로그래밍은 병렬화 프로그래밍이라고 할 수 있다.
병렬화도 잘못 짜게 되면 resource에 대한 race condition을 유발 할 수도 있지만, OpenMP같은 라이브러리만을 이용해서 활용한다면, 그 문제가 거의 발생하지 않는다.
위 그림은 concurrent 와 parallelism 의 차이를 보여준다
비동기 프로그래밍은 하나의 작업의 흐름에서 프로그램의 수행이 블록되지 않고, 계속 실행하게 만드는 기법이다.
프로그램이 블록 되는 이유는 대부분이 I/O 처리때문이다. (HDD, Network, Display, Mouse, …)
예를 들어서 아래와 같은 코드를 보자.
read_storage(buffer, 2000);
위 코드는 저장장치에서 2000바이트를 읽어서 buffer에 저장하라는 코드이다. (pseudo code임)
위 코드는 2000바이트를 읽을 때 까지 해당 코드가 멈추게 된다. 그냥 하릴없이 멈춘다. 저장장치가 2000바이트를 다 줄때까지…
그런데 해당 함수를 호출한 thread만 멈춘다. 다른 thread는 안 멈춘다.
다른 thread에는 영향을 안주고 자신만 멈추니까 비동기 프로그래밍의 매력이 별로 없어보인다.
GUI 프로그래밍을 생각해보자. 위 read_storage 처리 함수와 GUI 의 다양한 처리를 하는 함수들이 같은 thread에서 동작한다고 생각해보자. 그러면 저 함수가 리턴될 때 까지 화면은 멈추게 되고, 응답없음 이란 글자를 보게될 것이다.
그래서 과거에는 GUI 프로그래밍을 할 때 GUI 를 처리하는 thread에서는 저런 block이 발생하는 작업을 안하고 다른 thread에게 시키던지, non-block 함수를 써서 계속 polling으로 I/O 처리가 끝났는지 확인하곤 했다.
위 그림은 동기,비동기 / 블럭,논블럭 차이에 대한 설명이다.
사실 비동기 프로그래밍은 병렬화, 동시성 프로그래밍 하고는 같은 층위의 개념은 아니다. 병렬화, 동시성 개념은 다수의 작업을 코어에 어떻게 할당하는가에 대한 논의라면, 비동기 프로그래밍은 하나의 작업이 멈출지, 말지에 대한 논의이다. 좀 더 하위 개념에 가까운것 같다.
그럼에도 불구하고 비동기 프로그래밍은 multi-thread 프로그래밍에 좋은 대안이다. 실제로 multi-thread 프로그래밍보다 비동기 프로그래밍이 성능이 좋다. (정말?)
그 예시가 바로 node.js 이다. 참조( http://www.nextree.co.kr/p7292/ )
비동기 프로그래밍의 가장 큰 예시는 callback, EventEmitter 이다.
c++에서 템플릿의 구현체는 기본적으로 해더파일에 구현해야 한다.
템플릿의 내용을 cpp안에 넣어도 해당 코드를 라이브러리 형태로 다른곳에서 참조하지만 않으면 문제가 없다.
문제는 해당 cpp파일을 라이브러리(.a)로 만든다음에, 다른 프로그램이 링킹하려고 한다면 심볼을 찾을 수 없다는 오류가 발생한다.
예를 들어 아래와 같은 경우다.
//template.h class NSFinder { public: template <typename T> static T getParam(T val); }; //template.cpp template <typename T> T NSFinder::getParam(T val) { return val } //other program int main() { //에러, 함수를 찾을 수 없다 int temp = NSFinder::getParam<int>(1); }
가장 올바른 해결책은 h에 템플릿 코드를 넣는것이다. 하지만 이 방법말고도 가능한 방법이 있다.
1.라이브러리 코드 안에서 컴파일시에 필요한 템플릿 객체 강재로 생성하기.
아래 코드 처럼 템플릿이 존재하는 cpp안에서 필요한 타입의 템플릿 객체를 이용하면, 라이브러리안에 실체가 만들어지기 때문에 링크 에러가 발생하지 않는다. 단점은, 명시적으로 어떤 객체를 사용할지 선언해준 범위에서만 사용가능 하다.
//template.h class NSFinder { public: NSFinder(); template <typename T> static T getParam(T val); }; //template.cpp template <typename T> T NSFinder::getParam(T val) { return val } NSFinder::NSFinder() { //라이브러리 코드 안에서 한번 사용한다. int temp = NSFinder::getParam<int>(1); } //other program int main() { //빌드 성공 int temp = NSFinder::getParam<int>(1); //이건 실패한다 double temp = NSFinder::getParam<double>(1.0); }
2.템플릿 특수화를 이용해서 만들어준다. 이 방법은 코드가 중복되는 단점과, 명시적으로 어떤 객체를 사용할지 선언해준 범위에서만 사용 가능하다.
//template.h class NSFinder { public: NSFinder(); template <typename T> static T getParam(T val); }; //template.cpp //템플릿 특수화 template <> int NSFinder::getParam(int val) { return val } template <typename T> T NSFinder::getParam(T val) { return val } //other program int main() { //빌드 성공 int temp = NSFinder::getParam<int>(1); //이건 실패한다 double temp = NSFinder::getParam<double>(1.0); }
여러모로 불편하니, 그냥 해더파일에 템플릿을 구현하는게 정답이다
WPF의 MVVM 구조는 멋지다. GUI 개발에서 glue code를 많이 제거해준다.
특히 Model 의 변화가 자동으로 View 에 반영되는 구조는 매우 유용하다.
다만, 모델의 프로퍼티가 변경 되면 propertyChanged를 이용해서 변경된 사실을 통보해줘야 한다.
요게 은근히 귀찮다.
public class Person : INotifyPropertyChanged { string givenNames; public string GivenNames { get { return givenNames; } set { if (value != givenNames) { givenNames = value; OnPropertyChanged("GivenNames"); } } } }
위 코드처럼 프로퍼티를 일일이 만들어 줘야 하고, set 프로퍼티 안에서 일일이 OnPropertyChanged()를 호출해 줘야 한다. 하나 두개면 상관없지만 많으면 코드도 길어지고 개 귀찮..
하지만 역시 해결책은 있다!
https://github.com/Fody/PropertyChanged
이거 하나면 해결 끝~
ps. fody 라이브러리는 저거 말고도 엄청 다양한 플러그인이 있다
mbed 잘 쓰고 있다가 자꾸 서버가 다운되고 느려지고 해서 고민끝에 mbed를 탈출 하기로 마음먹고 KAIL uVision으로 이사 작업을 진행했다.
진행하는 도중 Eigen 라이브러리를 ARMCC에서 빌드해보니
routine is both “inline” and “noinline”
위 경고가 엄청 많이 뜬다. 사실 저 경고는 다른 컴파일러에서도 발생하는데 Eigen의 DisableStupidWarnings.h 파일에서 컴파일러 지시문을 이용해서 없애고 있을 뿐이다.
ARMCC는 저 경고가 제거되지 않고 계속 나와서 없애는 법을 찾아보았다.
#pragma diag_suppress 2751
위 지시문을 지정해주면 2751번 Warning이 무시된다. 비슷한 지시문으로는
#pragma diag_error 2751
위 지시문을 지정해지면 2751번 Warning이 Error로 격상되서 컴파일 오류를 발생시킨다.
참고로 MSVC에서의 경고문 제거 지시문은 아래와 같다
#pragma warning( disable : 4100 )
참고자료
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bk/CIHECIAD.html
PyCharm에서 가장 멋진 기능중에 하나는 원격 서버에서 실행하고 디버깅 할 수 있는 기능이다. 그 뿐만 아니라 원격지 서버에 있는 파이썬의 모든 라이브러리 정보를 로컬로 가져와서 코딩시에 자동완성이나 라이브러리 정보등을 볼수도 있다. 이를 통해서 파이썬기반 ROS 프로그래밍을 하는 방법을 살펴보자
우선 원격지에 있는 파이썬 인터프린터를 PyCharm에 등록해야 한다.
Preference > Project Interpreter > 오른쪽 상단에 있는 톱니바퀴 > Add Remote
아래와 같은 창이 뜨는데 자신의 원격지 서버 정보를 정확하게 입력하고 OK를 누르면 원격지 서버에 접속해서 PyCharm Helper를 전송하고 라이브러리 정보를 다운 받아서 파싱을 진행하는 과정을 거친다.
문제는 대부분의 필요한 파이썬 라이브러리 정보를 분석하지만 ROS 파이썬 라이브러리는 분석하지 않는다. 사용자가 직접 ROS 파이썬 라이브러리 위치를 등록해줘서 PyCharm이 ROS 파이썬 라이브러리도 인식할 수 있도록 해줘야 한다.
Preference > Project Interpreter > 오른쪽 상단에 있는 톱니바귀 > More…
리스트 목록 중에 방금 자신이 등록한 원격지 인터프린터가 있을 것이다. 해당 인터프린터를 선택한 다음, 아래 아이콘을 클릭한다.
그러면 원격지 서버에 어떤 디렉토리에 있는 파이썬 라이브러리를 분석할지 리스트가 나오는데 여기에다가 사용자가 직접 ROS 파이선 라이브러리들이 위치한 디렉토리를 추가해 주도록 한다.
이번에는 로컬에서 코딩한 파일을 원격지 서버에 배포하는 방법을 살펴보자. 프로젝트를 하나 만들어서 새로운 파일에 적당히 소스를 코딩하자. 그리고 아래 메뉴로 들어간다.
Tools > Deployment > Configuration…
왼쪽 상단에 있는 + 아이콘을 눌러서 새로운 서버를 추가한다. SFTP를 이용하면 SSH를 이용하기 때문에 편하다. 서버 주소와 계정, 비밀번호를 정확하게 입력하고, Root path 정보는 / 라고만 입력한다.
그리고 나서 상단에 있는 Mappings 탭을 눌른다. Local Path는 로컬에 소스가 있는 위치를 지정하고 Deployment path on server는 원격지에 파일을 저장할 위치를 지정한다. 적당한 위치를 지정해준다. 그리고 OK를 누르고 창을 빠져나온다.
Tools > Deployment > Automatic Upload
위 기능을 활성화 해놓으면 파일 수정후 저장시 자동으로 파일을 원격지 서버에 업로드 해준다.
Run > Edit Configurations…
위 메뉴에 들어가서 왼쪽 상단에 있는 + 버튼을 누른다. 종류는 Python을 선택한다. Name은 적당히 적어준다. Script 에는 해당 프로젝트의 엔트리 포인트가 되는 파일명을 적어준다. Working Directory에는 소스가 있는 로컬 디렉토리를 지정해준다. OK를 누르고 나오면 된다.
이렇게 하면 대부분의 파이선 개발에는 문제가 없다. 그런데 ROS 프로그램 개발시에는 제대로 동작하지 않는다. 이는 환경 변수가 제대로 설정되지 않아서 그런것인데, 이를 해결하기 위한 깔끔한 방법을 아직 찾지 못했다. 대신 아래와 같은 코드를 엔트리 포인트에 추가해주면 ROS 파이썬 프로그램을 동작시킬 수는 있다. 아래 내용을 컴퓨터에 따라서 전부 다르므로 자신의 환경 변수를 참고해서 적절하게 입력해야 한다.
os.environ['PKG_CONFIG_PATH'] = "/home/torooc/catkin_ws/devel/lib/pkgconfig:/home/torooc/catkin_ws/devel/lib/arm-linux-gnueabihf/pkgconfig:/opt/ros/jade/lib/pkgconfig:/opt/ros/jade/lib/arm-linux-gnueabihf/pkgconfig" os.environ['ROS_DISTRO'] = "jade" os.environ['ROS_PACKAGE_PATH'] = "/home/torooc/catkin_ws/src:/opt/ros/jade/share:/opt/ros/jade/stacks" os.environ['ROS_ETC_DIR'] = "/opt/ros/jade/etc/ros" os.environ['ROS_ROOT'] = "/opt/ros/jade/share/ros" os.environ['ROS_MASTER_URI'] = "http://localhost:11311"
스샷에서 보이는 아이콘을 누르면 되는데 기본적으로 저 아이콘이 보이지가 않는다.
도구 > 옵션 > 프로젝트 및 솔루션 > VC++ 프로젝트 설정 > 솔루션 탐색기 모드 에서
“모든 파일 표시”로 설정을 바꿔주면 해당 아이콘이 표시된다
오류) 메이븐 기반 프로젝트를 Tomcat으로 띄울려고 Run을 누르면 log4j 라이브러리를 찾을 수 없다고 에러가 난다
java: package org.apache.log4j does not exist
해결책) 이 오류는 IntelliJ에서 자동으로 만드는 xxx.iml 파일 내용중에 log4j 의 scope가 RUNTIME으로 되어 있어서 그렇다.
<orderEntry type="library" scope="RUNTIME" name="Maven: log4j:log4j:1.2.14" level="project" />
scope=”RUNTIME”을 지워준다