JUnit과 Mockito 기반 Spring 단위 테스트 코드 : https://mangkyu.tistory.com/145
Mock 개념 : https://www.crocus.co.kr/1555
Java 기반 Mock 사용하기(Mockito) : https://www.crocus.co.kr/1556
JUnit에서 Private 변수 접근하여 테스트 : https://www.crocus.co.kr/1665
JUnit에서 System.out.println 출력 내용 테스트 : https://eblo.tistory.com/123 
Intellij IDEA에서 Junit 사용하기 : https://ildann.tistory.com/5

 

 

 

 

 

Posted by 꿈만은공돌
,

 

 단위테스트(Unit Test)를 작성하는데 AAA 패턴을 이용해서 작성하면 단순하고 균일한 구조를 갖는데 도움을 주며 가독성 또한 좋아진다. 

 

 AAA패턴이란 준비(arrange), 실행(act), 검증(assert) 세 단계로 Test를 작성하는 패턴을 의미한다.

 

- 준비(arrange) : 테스트에 필요한 변수나 객체를 생성한다. 필요에 따라 mock 객체를 만든다.

 

- 실행(act) : 테스트 할 코드를 실행해 본다.

 

- 검증(assert) : 실행한 코드가 설계한대로 정확하게 동작했는지를 검증해본다. Unit Test의 Api인 assertEqals() 등의 코드를 사용하여 검증한다.

 

비슷한 패턴으로 Given-When-Then 패턴이 존재 한다.

 

아래 코드는 AAA패턴을 이용해서 Test 코드를 작성한 것이다.

 

import static org.junit.Assert.*;
import org.junit.Test;

public class MyMathTest {
	@Test
	public void mul_of_two_numbers() {
		// arrange
		MyMath math = new MyMath();
		int first = 5;
		int second = 30;	
		
		// act
		int result = math.mul(first, second);
		
		// assert
		assertEquals(150, result);
	}
}

 

각 단계마다 코드의 길이에 대한 설명이다.

 

 

-  준비(arrange) : 필요한 객체를 생성하고 필요한 변수들을 셋팅하는 과정이기 실행이나 검증 단계보다 코드가 길어질 수 있다. 준비 단계 코드가 너무 길어지고 다른 테스트와의 유사성이 있다면 중복된 부분을 private 함수를 이용해서 리팩토링하는 것도 좋은 방법이다.

 

- 실행(act) : 보통 한줄의 코드이다. 실행 구절이 두줄 이상이라면 잘못 설계된 API일 가능성이 높다.

 

- 검증(assert) : 유닛 테스트란 단일 동작을 검증하는 단위테스트다. 하지만 단일 동작으로 여러 결과를 낼 수 있기 때문에 코드가 길어 질 수 있다.

 

 

 

 

Posted by 꿈만은공돌
,

JAVA 코드를 유닛 테스트 하기위하여 Junit을 이용하여 Eclipse 환경에서의 방법을 알아보자.

이클립스(Eclipse) 버전은 2022-03(4.23.0) 버전을 사용하였다.

JDK는 18.0.1.1 버전을 사용하였다.

최근 몇년간의 버전이라면 아래 방법과 큰차이가 없을 것이다.

외부라이브러리를 번거롭게 수동으로 추가해주지 않아도 된다.

 

이클립스 버전 정보

 

File -> New -> Java Project 를 실행하여 아래와 같은 팝업창에서 프로젝트 명을 입력하고 Finish 버튼을 누른다.

 

신규 자바 프로젝트 생성

 

그 이후에 새로 만들어진 프로젝트에 새로운 Class를 생성한다.

프로젝트에 Src를 클릭하고 오른쪽 버튼을 눌러서 Class를 선택한다. 그러면 아래와 같은 팝업창이 뜨는데 class이름을 입력하고 Finish를 누른다. 

 

Class 생성

 

그리고 아래와 같이 클래스를 작성한다.

 

소스코드 작성

 

그 이후에 프로젝트의 Src에 오른쪽 버튼을 누른 후 New-> Junit Test Case를 선택한다.

 

JUnit Test Case 생성

 

그러면 아래와 같은 팝업창이 뜨는데 New JUnit 4 test를 선택하고 Name을 입력하고 Finish를 누른다.

 

 

 

 

그러면 JUnit 라이브러리가 없으니 추가하라고 나오는데 아래와 같이 선택하고 OK 버튼을 누른다. 

 

JUnit4 라이브러리 추가

 

그러면 아래와 같이 테스트 클래스가 생성된다.

 

 

이 테스트 클래스를 실행하기 위해서는 테스트클래스에 오른쪽 버튼을 눌러 Run As -> JUnit Test 를 클릭한다.

 

 

그러면 아래와 같이 실패했다는 정보를 확인할 수 있다.

 

Junit Test 실패

 

아래와 같이 테스트 클래스의 코드를 변경한다.

 

import static org.junit.Assert.*;
import org.junit.Test;

public class MyMathTest {
	@Test
	public void test() {
		MyMath math = new MyMath();
		assertEquals(10, math.add(3, 7));
	}
}

 

 

그이후에 다시 실행하게 되면 아래이미지와 같이 성공으로 뜬것을 확인할 수 있다.

 

Junit Test 성공

 

Posted by 꿈만은공돌
,

문자열을 자르는 함수에 대해서 정리해보자

프로그래밍 언어마다 문자열을 자르는 함수의 사용방법이 조금씩 다른경우가 많다.

string 의 멤버 함수로 substring()과 split() 두가지가 가장 흔하게 쓰이는 방식이다.

 

첫번째로 substring() 함수는 시작지점과 끝지점을 파라미터로 전달받아서 문자열을 자르게 된다.

substring(시작지점, 끝지점) 이런식으로 사용하면 된다. 

아래이미지를 보도록 하자.

 

 

substring(1, 3) 을 입력하면 인덱스가 1부터 3까지의 글자를 자르게 되는데 B부터 C까지 잘라서 "BC"가 리턴이 된다.

substring(5) 를 입력하면 인덱스 5부터 문자열 끝까지 잘라서 "3de"가 리턴된다.

 

아래 사용 예제를 보도록 하자.

public static void main(String[] args) {		
	String str = "abcdefg12345";
	System.out.println("substring(5) : " + str.substring(5));
	System.out.println("substring(0,1) : " + str.substring(0,1));
	System.out.println("substring(1,2) : " + str.substring(1,2));
	System.out.println("substring(2,3) : " + str.substring(2,3));
	System.out.println("substring(2,10) : " + str.substring(2,10));
}

출력 :

substring(5) : fg12345
substring(0,1) : a
substring(1,2) : b
substring(2,3) : c
substring(2,10) : cdefg123

 

 

만약 문자열 끝에서부터 3글자만 출력하고 싶다면 아래 예제와 같이 사용하면 된다.

public static void main(String[] args) {		
	String str = "abc123def";
	System.out.println(str.substring(str.length()-3, str.length()));
}

출력 :

def

 

그리고 아래예제와 같이 indexOf() 함수를 사용하면 특정 글자 두개를 포함하여 그사이에 글자를 모두 출력하게 된다.

 

public static void main(String[] args) {		
	String str = "abc123def";
    System.out.println(str.substring(str.indexOf("1"), str.indexOf("3") + 1));
}

출력 :

123

 

그런데 substring() 함수를 사용하다가 문자열의 길이보다 긴 값을 substring의 인자로 전달하게 되면 아래 예제와 같이 StringIndexOutOfBoundsException 에러가 발생하게 된다. 총 글자수는 12개이지만 10부터 20을 출력하라고 했기때문에 에러가 발생하였다.

 

public static void main(String[] args) {		
	String str = "abcdefg12345";
	System.out.println("substring(10,20) : " + str.substring(10,20));
}

에러 메시지 :

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [10, 20) out of bounds for length 12
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4589)
at java.base/java.lang.String.substring(String.java:2703)
at TestSubString.main(TestSubString.java:5)

 

 

 

이것을 방지하기 위해서는 아래예제와 같이 StringIndexOutOfBoundsException 에러를 예외처리해주면 된다.

 

public static void main(String[] args) {		
	String str = "abcdefg12345";
	try {
		System.out.println("substring(10,20) : " + str.substring(10,20));
	} catch(StringIndexOutOfBoundsException e) {
		System.out.println("Error : "+e.getMessage());
	}		
}

출력 : 

Error : Range [10, 20) out of bounds for length 12

 


 

두번째로 split() 함수는 특정 문자열을 파라미터로 받아서 해당 문자열을 기준으로 문자열을 잘라서 배열에 넣어주는 기능을 한다. String 의 멤버함수이다. 

 

아래이미지를 보면 이해하기 쉬울 것이다.

 

 

위에 이미지를 아래는 코드화 한것이다.

 

public static void main(String[] args) {		
	String str = "ABC,EE,QQ1,5112";
	String arr[] = str.split(",");
	for(String cut : arr) {
		System.out.println(cut);
	}		
}

출력 :

ABC
EE
QQ1
5112

 

 

Posted by 꿈만은공돌
,

 

프로그래밍을 하다보면 특히 게임쪽에서 랜덤(random) 또는 난수 를 생성할 일이 있다.
난수는 무작위 숫자를 뜻하지만 컴퓨터 쪽에선 100% 무작위(랜덤)은 아니다.
정해진 난수표에서 숫자를 가저오는 방식이기 때문이다.


자바(JAVA) 에서 난수를 생성하는 방식은 크게 두가지 이다.


첫번째로 Math.random() 이용하는 방식이다.
Math Class의 random 함수는 double 형 타입으로 0.0 이상 1.0 미만 사이의 값을 반환한다.

 

public class TestRandom {
    public static void main(String[] args) {        
        for(int i =0; i < 10; i++) {
            System.out.print(Math.random()+" ");
        }
        System.out.println("");
    }
}

 

* 결과 값 :

0.1652487713090327 0.286398184817342 0.4336343206580924 0.4295581874855847 0.44636521032290466 0.8715008193305886 0.93917325018134 0.5647002564584279 0.7323427296788428 0.8295767550811228

 

보통은 자연수의 난수가 필요한 경우가 많기 때문에 아래 예제 처럼 0부터 9까지의 난수가 필요하다면 x10을 한 이후에 int형으로 강제 형변환을 시켜주면 된다.

 

public class TestRandom {
    public static void main(String[] args) {        
        for(int i =0; i < 10; i++) {
            System.out.print((int)(Math.random()*10) + " ");
        }
        System.out.println("");
    }
}

 

* 결과 값 :

2 6 3 7 6 9 8 7 9 7 


하지만 이 방식은 직관적이지 않고 형변환등을 하면서 익셉션(Exception) 처리등을 해주어야 하기 때문에 두번째 방식을 추천한다.

 


 

 

 

 

두번째로 Random Class를 이용하는 방식이다.
난수를 생성하는데 필요한 기능 등을 묶어놓은 클레스 이다.
Random Class를 사용하려면 import java.util.Random; 를 해주어야 한다.
Random Class의 맴버변수중에 setSeed를 이용하거나 생성자에 매개변수로 시스템시간을 매개로 전달하면 난수표의 시드값을 초기화 할 수 있다.
아래는 0부터 9까지의 정수를 출력하는 예제이다.

 

import java.util.Random;
 
public class TestRandom {
    public static void main(String[] args) {        
        Random random = new Random(System.nanoTime());
        for(int i =0; i < 10; i++) {
            System.out.print((int)(random.nextInt(10)) + " ");
        }
        System.out.println("");
    }
}


* 결과 값 :


1 3 3 7 6 9 5 7 2 4


아래는 1부터 45까지의 숫자를 출력하는 로또 번호 추첨 예제이다. 랜덤으로 숫자를 추첨하는데 기존에 나왔던 숫자가 있을 경우 다시 재추첨하도록 하였다.

 

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
public class TestRandom {
    public static void main(String[] args) {        
        Random random = new Random(System.nanoTime());
        List<Integer> result = new ArrayList<Integer>();
        int num;
        for (int i = 0; i < 6; i++) {
            while (true) {
                num = random.nextInt(45) + 1;
                if (result.contains(num)) {
                    continue;
                } else {
                    result.add(num);
                    System.out.print(num+" ");
                    break;
                }
            }
        }    
        System.out.println("");
    }
}

 

* 결과 값 :

 

12 38 33 29 4 17 

 

 

아래 테이블은 다양한 Random class의 멤버함수 들이다. 이외에도 다양한 함수들이 많이 존재하니 라이브러리를 참고하도록 하자.

함수 설명
nextInt(int i) 0부터 i 까지의 랜덤한 숫자를 리턴
nextInt() Int 타입의 최소 ~ 최대범위 안에서 랜덤한 숫자를 리턴
nextLong() Long 타입의 최소 ~ 최대범위 안에서 랜덤한 숫자를 리턴
nextDouble() Double 타입의 0.0 – 1.0까지의 랜덤한 숫자를 리턴
nextBoolean() boolean타입의 true, false 중 랜덤한 값을 리턴

 

참고 자료 : https://docs.oracle.com/javase/8/docs/api/java/util/Random.html

Posted by 꿈만은공돌
,

 마이크로소프트 윈도우10에서 Visual Studio 2017로 개발할 때 여러 앱을 개발할때 원활한 개발환경을 제공받기 위해서는 윈도우 개발자 모드를 켜야 한다.

특히 UWP 앱을 개발하려면 반드시 활성화를 해야 한다. 윈도우 마켓에서 허가되지 않은 앱도 실행이 가능하기 때문이다.


개발자 모드를 활성화 하기 위한 방법이다.


1. Windows 설정에 들어가 업데이트 및 보안을 선택한다.

 



2. 왼쪽 택에서 개발자용을 선택한다.




3. 개발자 모드를 선택한다.


Posted by 꿈만은공돌
,

멀티스레드에서 하나의 자원에 접근할 때 동기화 이슈가 발생할 수 있습니다. 

멀티스레드에 관한것은 아래 링크를 참고하자.

2018/07/15 - [닷넷,C#,.NET] - C# 멀티 태스킹 구현, 비동기 태스크


그래서 여러 스레드가 하나에 자원에 접근할 때 하나가 해당 자원을 점유하고 있으면 다른 스레드에서는 접근을 못하고 대기하게 해야한다.


 아래 예제는 변수 num 에 두개으 스레드가 접근하여 하나의 스레드(MethodA)는 num값을 하나 증가시키고 다른 스레드(MethodB)는 num 값을 감소시킨다.

그래서 5만번씩 증가와 감소를 하면 num의 최종값은 0이되야 한다. 하지만 실제 값은 0이 아닌 다른 값이 나올때가 많다. 매번 값은 다르다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static int num = 0;
        static void MethodA()
        {
            for (int i = 0; i < 50000; i++)
            {                
                    num++;                
            }
        }
        static void MethodB()
        {
            for (int i = 0; i < 50000; i++)
            {
                num--;
            }
        }
 
        static void Main(string[] args)
        {
            Stopwatch watch = Stopwatch.StartNew(); //시간 측정 시작
            Task aTask = Task.Factory.StartNew(MethodA);
            Task bTask = Task.Factory.StartNew(MethodB);
            Task.WaitAll(new Task[] { aTask, bTask });
            watch.Stop(); //시간 측정 완료
            Console.WriteLine("소요시간 : " + 
                            watch.ElapsedMilliseconds + "s");
            Console.WriteLine("num : " + num);
        }
    }
}
cs



 그래서 이와 같은 동기화 문제를 해결하기 위해서는 lock으로 num을 접근하는 코드에 넣어서 해결할 수 있다.

 아래 예제는 동기화 이슈를 해결한 코드이다.

 물론 소요시간은 위에 예제보다 더 많이 걸리게 된다. lock이 걸리면 다른 스레드는 대기하기 때문에 시간이 오래 걸리는 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static int num = 0;
        static object conch = new object();
        static void MethodA()
        {
            for (int i = 0; i < 50000; i++)
            {
                lock (conch)     //잠금
                {
                    num++;
                }    //풀림
            }
        }
        static void MethodB()
        {
            for (int i = 0; i < 50000; i++)
            {
                lock (conch)     //잠금
                {    
                    num--;
                }    //풀림
            }
        }
 
        static void Main(string[] args)
        {
            Stopwatch watch = Stopwatch.StartNew(); //시간 측정 시작
            Task aTask = Task.Factory.StartNew(MethodA);
            Task bTask = Task.Factory.StartNew(MethodB);
            Task.WaitAll(new Task[] { aTask, bTask });
            watch.Stop(); //시간 측정 완료
            Console.WriteLine("소요시간 : " + watch.ElapsedMilliseconds + "s");
            Console.WriteLine("num : " + num);
        }
    }
}
 
cs


 이런 동기화 이슈가 발생하는 이유는 A스레드에서 변수 num값을 읽어와서 증가시킨 다음에 다시 num변수에 값을 쓴다. A스레드가 num값을 읽어간 동안 스레드 B가 num값을 다시 읽어가면 원하지 않는 값을 쓰게 된다.

 C# 코드로는 num++ 한줄이지만 어셈블리 코드로는 여러줄이 되게 된다. 그래서 이러한 문제를 lock 하는 방법 말고 Interlocked 타입을 이용해서 값을 증가하거나 감소하는 코드를 대신해서 사용하면 이런 문제를 해결할 수 있다.

 아래 예제를 보자. 위의 예제와 같은 결과를 얻을 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static int num = 0;
        static object conch = new object();
        static void MethodA()
        {
            for (int i = 0; i < 50000; i++)
            {
                Interlocked.Increment(ref num);
            }
        }
        static void MethodB()
        {
            for (int i = 0; i < 50000; i++)
            {
                Interlocked.Decrement(ref num);
            }            
        }
 
        static void Main(string[] args)
        {
            Stopwatch watch = Stopwatch.StartNew(); //시간 측정 시작
            Task aTask = Task.Factory.StartNew(MethodA);
            Task bTask = Task.Factory.StartNew(MethodB);
            Task.WaitAll(new Task[] { aTask, bTask });
            watch.Stop(); //시간 측정 완료
            Console.WriteLine("소요시간 : " + watch.ElapsedMilliseconds + "s");
            Console.WriteLine("num : " + num);
        }
    }
}
cs

Posted by 꿈만은공돌
,

C#에서 비동기로 병렬 처리하는 방법에 대해서 알아보겠다.


1. 일반적인 함수호출(단일 스레드)

아래 코드는 3개의 함수(FuncA, FuncB, FuncC)를 순서대로 호출하는 코드이다.

세가지의 함수가 단일스레드로 호출하는데 총 걸리는 시간을 측정하여 출력한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace T
{
    class Program
    {
        static void FuncA()
        {
            Console.WriteLine("A함수 시작");
            Thread.Sleep(1000);
            Console.WriteLine("A함수 끝");
        }
        static void FuncB()
        {
            Console.WriteLine("B함수 시작");
            Thread.Sleep(2000);
            Console.WriteLine("B함수 끝");
        }
        static void FuncC()
        {
            Console.WriteLine("C함수 시작");
            Thread.Sleep(3000);
            Console.WriteLine("C함수 끝");
        }
        static void Main(string[] args)
        {
            var timer = Stopwatch.StartNew(); //시간측정 시작
            FuncA();           //A함수 호출
            FuncB();           //B함수 호출
            FuncC();           //C함수 호출
            timer.Stop();      //시간 측정 끝
            Console.WriteLine(timer.ElapsedMilliseconds + "s");
        }
    }
}
 
 
cs

- 출력 결과 -

A함수 시작

A함수 끝

B함수 시작

B함수 끝

C함수 시작

C함수 끝

6005s



2. 비동기 멀티 테스크 만들기

Thread 클르스를 이용하면 스레드를 만들고 관리할 수 있다. 하지만 이 Thread 클레스는 직접 사용하기가 까다로운 점이 많다.

그래서 C# 4.0부터 추가된 Task 클래스를 사용하면 스레드를 쉽게 생성하고 관리 할 수 있다.

아래 예제는 비동기로 3가지 함수를 각각 실행한다.

Task 클래스를 사용하여 객체를 선언하고 Start함수로 태스크를 시작한다. 그러면 스레드가 특정한 순서 없이 동시에 실행이 된다. 그래서 실행순서는 보장하지 않고 끝나는 시점은 딜레이가 가장 짧은 FuncA부터 종료하게 된다. Wait 함수로 해당 태스크가 종료될때까지 해당 코드에서 대기한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static void FuncA()
        {
            Console.WriteLine("A함수 시작");
            Thread.Sleep(1000);
            Console.WriteLine("A함수 끝");
        }
        static void FuncB()
        {
            Console.WriteLine("B함수 시작");
            Thread.Sleep(2000);
            Console.WriteLine("B함수 끝");
        }
        static void FuncC()
        {
            Console.WriteLine("C함수 시작");
            Thread.Sleep(3000);
            Console.WriteLine("C함수 끝");
        }
        static void Main(string[] args)
        {
            Task taskA = new Task(FuncA); //태스크 객체
            Task taskB = new Task(FuncB);
            Task taskC = new Task(FuncC);
            var timer = Stopwatch.StartNew(); //시간측정 시작
            taskA.Start();         //태스크 A시작
            taskB.Start();         //태스크 B시작
            taskC.Start();         //태스크 C시작
            taskA.Wait();         //태스크 A 끝날때까지 대기
            taskB.Wait();         //태스크 B 끝날때까지 대기
            taskC.Wait();         //태스크 C 끝날때까지 대기
            timer.Stop();         //시간 측정 끝
            Console.WriteLine(timer.ElapsedMilliseconds + "s");
        }
    }
}
cs


- 출력 결과 -

C함수 시작

B함수 시작

A함수 시작

A함수 끝

B함수 끝

C함수 끝

3008s



3. 다양한 방법으로 태스크 만들기

위의 예제에서는 Task객체를 각각 생성해서 Start함수를 호출하였지만 아래 예제에서는 Task.Factory.StartNew(FuncA) 와 같은 방식이나 Task.Run(new Action(FuncB))와 같은 방식으로 객체를 생성과 동시에 Task를 시작시킨다.

그리고 각각의 Task들에 Wait함수를 호출하여 종료를 대기했지만 아래 예제에서는 Task 배열을 만들고 Task.WaitAll() 함수에 Task 배열을 파라미터로 넘겨줘서 한번에 대기시킬 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static void FuncA()
        {
            Console.WriteLine("A함수 시작");
            Thread.Sleep(1000);
            Console.WriteLine("A함수 끝");
        }
        static void FuncB()
        {
            Console.WriteLine("B함수 시작");
            Thread.Sleep(2000);
            Console.WriteLine("B함수 끝");
        }
        static void FuncC()
        {
            Console.WriteLine("C함수 시작");
            Thread.Sleep(3000);
            Console.WriteLine("C함수 끝");
        }
        static void Main(string[] args)
        {
            var timer = Stopwatch.StartNew(); //시간측정 시작
            Task taskA = Task.Factory.StartNew(FuncA);  //쓰레드 A 생성 후 시작
            Task taskB = Task.Run(new Action(FuncB));   //쓰레드 B 생성 후 시작
            Task taskC = Task.Factory.StartNew(FuncC);  //쓰레드 C 생성 후 시작
            Task[] tasks = {taskA, taskB, taskC};       //쓰레드 종료까지 대기
            Task.WaitAll(tasks);
            timer.Stop(); //시간 측정 끝
            Console.WriteLine(timer.ElapsedMilliseconds + "s");
        }
    }
}

cs


- 출력 결과 - 

C함수 시작

A함수 시작

B함수 시작

A함수 끝

B함수 끝

C함수 끝

3008s



4. A스래의 결과를 B스래드에 넘겨주기

A스레드를 실행하여 결과를 받아서 B스레드를 실행하면서 값을 넘겨주는 예제이다.

ContinueWith 함수를 사용한다.

TestA 함수를 StartNew 함수로 태스크를 만들어서 스래드를 실행하고 그 결과 값을 ContinueWith 함수로 TestB에 Result로 결과 값을 넘겨준다. 값을 받아 새로운 스레드를 만들어 실행한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Threading;
using System.Threading.Tasks;
 
namespace T
{
    class Program
    {
        static int TestA()
        {
            Console.WriteLine("TestA Start");
            Thread.Sleep(1000);
            Console.WriteLine("TestA End");
            return 10;
        }
        static int TestB(int i)
        {
            Console.WriteLine("TestB Start");
            Thread.Sleep(1000);
            Console.WriteLine("TestB End");
            return i + 10;
        }
        static void Main(string[] args)
        {
            var test = Task.Factory.StartNew(TestA)
                                .ContinueWith(task=>TestB(task.Result));
            Console.WriteLine("result : " + test.Result);
        }
    }
}
 
cs


- 출력 결과 -

TestA Start

TestA End

TestB Start

TestB End

result : 20


5. 중첩 태스크(Task)

아래 예제는 2개의 Task가 중첩되서 실행하는 코드이다.

Inner 태스크가 3초 딜레이가 있어 Outer 태스크가 먼저 종료되고 Inner 태스크가 종료된다. 

Inner 태스크가 완료되기를 기다리지 않는다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static void Main(string[] args)
        {
            
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task 시작");
                var inner = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Inner task 시작.");
                    Thread.Sleep(2000);
                    Console.WriteLine("Inner task 종료.");
                });            
            });
            outer.Wait();
            Console.WriteLine("Outer task 종료.");
            Console.ReadLine();
        }
    }
}
cs


- 출력 결과 -

Outer task 시작

Outer task 종료.

Inner task 시작.

Inner task 종료.


Inner 태스크가 완료되기를 기달리고 Outer 태스크가 완료되게 하려면 Inner 태스크에 TaskCrationOption의 AttachedToParent 값을 추가해준다.

아래는 그 예제이다. Inner 클래스가 먼저 종료되는 것을 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace T
{
    class Program
    {
        static void Main(string[] args)
        {
            
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task 시작");
                var inner = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Inner task 시작.");
                    Thread.Sleep(2000);
                    Console.WriteLine("Inner task 종료.");
                }, TaskCreationOptions.AttachedToParent);            
            });
            outer.Wait();
            Console.WriteLine("Outer task 종료.");
            Console.ReadLine();
        }
    }
}
 
cs


- 출력 결과 -

Outer task 시작

Inner task 시작.

Inner task 종료.

Outer task 종료.


멀테스레드에서 동기화하는 방법은 아래 링크를 참고하자

2018/07/17 - [닷넷,C#,.NET] - C# 멀티스레드 공유 리소스 동기화



참고 자료 : 크로스 플랫폼 개발을 위한 C# 7과 닷넷 코어 2.0 책

Posted by 꿈만은공돌
,

샤오미 어메이즈 핏 빕에 대해서 리뷰하고 한글화에 대해서 설명하겠습니다.


가격은 약 6만원대 중반에서 후반정도 이며


애플워치와 비슷하게 생겼으며 기능은 시계, 만보기, 수면체크, 알림기능 등이 있습니다.


- 장점 -


가장큰 장점으로는 충전없이 배터리가 한달은 사용가능 합니다.


화면이 계속켜져있기 때문에 버튼을 누르거나 손목을 들어올려야지만 화면을 볼 수 있는 다른 스마트 워치보다 편리합니다.


시계화면을 자신에 입맛대로 수정하거나 다른 사람이 만든 화면들을 다운받아서 사용할 수 있습니다.


화면이 밝은데서 더 밝게보이고 어두운데서 어둡게 보여서 실외에서도 잘보이고 좋습니다.


잘때 차고가도 전혀 거슬리지 않습니다.



아래는 개봉기 입니다.



어메이즈 핏 빕은 한글화가 안되어 있기 때문이 한글 알림을 받으면 글자가 깨저서 나오게 됩니다.


그래서 직접 업데이트를 해줘야 합니다.


우선 애플 미핏 어플과 연동되어 있다면 연결을 해제 시켜줘야 합니다.


그리고 아래 첨부파일을 안드로이드 폰에 다운 받습니다.


어메이즈 핏 빕 한글.zip


안에는 가젯브릿지 설치 파일과 한글 폰트 파일 두가지가 들어있습니다.


압축을 풀고 아래 그림과 같이 가젯브릿지 파일을 설치해줍니다.

그리고 +버튼을 눌러서 어메이즈핏 과 연결시켜 줍니다.



이후 아래그림과 같이 다운받은 폰트파일을 눌러서 공유를 눌러줍니다.

그리고 APP선택 화면에서 가젯브리지를 선택하면 폰트 업데이트를 합니다.

업데이트가 완료되면 가젯브릿지 APP를 켜서 연결을 끊어줍니다.(오른쪽에 옆으로 누워있는 ...을 눌느후 삭제를 하면 됩니다).




그러면 아래 그림과 같이 한글 폰트가 정상적으로 나오는 것을 확인할 수 있습니다.

이후 미핏 어플에 연결해서 사용하면 됩니다.



메인화면을 변경하고 싶으면 아래 어플을 설치해서 사용하면 됩니다.

다양한 어플들이 많이 있습니다.




한글 관련 화면은 샤오미 스토리 라는 네이버 카페에 가면 많이 구할 수 있습니다.








Posted by 꿈만은공돌
,

C#에서는 여러가지 방식으로 인코딩 할 수 있도록 Encoding 클래스를 제공해준다.


System.Text 네임스페이스 선언을 해야한다.


Encoding 클래스에 .을 누르면 아래 그림과 같이 여러가지 지원하는 인코딩 방식을 볼 수 있다.



"ABC123 abc!" 문자열을 byte 배열로 인코딩 하는 예제이다.

ASCII(아스키2) 방식의 예제이다. 9번째줄 ASCII 대신에 UTF-8이나 기타 다른 방식에 인코딩 방식을 적어주면 여러 방식에 인코딩으로 변환이 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Text;
namespace TEST
{
    class Program
    {
        static void Main(string[] args)
        {
            Encoding encoding = Encoding.UTF8;
            string str = "ABC123 abc!";
            byte[] encoded = encoding.GetBytes(str);
            foreach(byte b in encoded){
                Console.Write(b+" ");
            }
            Console.WriteLine();
        }
    }
}
 
cs



- 출력 결과 -

65 66 67 49 50 51 32 97 98 99 33

Posted by 꿈만은공돌
,