반응형

여러 가지 비동기 스레딩을 구현해 보자.

 

우선 인수와 리턴값이 없는 스레드다.

아래 3가지 코드는 모두 같은 내용이지만 다른 방식으로 작성되었다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Counter()
        {
            int subCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }
        }

        static void Main(string[] args)
        {
            Task task = new Task(Counter);
            task.Start();
                        
            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");                
            }

            task.Wait();
        }
    }
}

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Action counter = () =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }
            };

            Task task = new Task(counter);
            task.Start();
                        
            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");                
            }

            task.Wait();
        }
    }
}

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = Task.Run(() =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Task counting... {subCounter++}");
                }
            });

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
        }
    }
}

 

순서는 약간 달라질 수 있다.

 

 

이번엔 인수를 하나 받는 스레드를 만들어 보자. 아래 2가지 코드는 모두 같은 내용이지만 다른 방식으로 작성되었다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Counter(object to)
        {
            int subCounter = 0;
            for (int i = 0; i < (int)to; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }
        }

        static void Main(string[] args)
        {
            Task task = new Task(Counter, 5);
            task.Start();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
        }
    }
}

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Action<object> Counter = (object to) =>
            {
                int subCounter = 0;
                for (int i = 0; i < (int)to; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }
            };

            Task task = new Task(Counter, 5);
            task.Start();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
        }
    }
}

 

인수가 있는 경우, Task.Run() 메서드에서 람다식으로 코드를 만드는 건 의미가 없는 것 같다.

결과는 위와 같다.

 

 

이번엔 리턴값이 있는 스레드를 만들어 보자. 아래 2가지 코드는 모두 같은 내용이지만 다른 방식으로 작성되었다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static int Counter()
        {
            int subCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }

            return subCounter;
        }

        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Counter);
            task.Start();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
            Console.WriteLine($"Sub Thread finished with count: {task.Result}");
        }
    }
}

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = Task<int>.Run(() =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }

                return subCounter;
            });

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
            Console.WriteLine($"Sub Thread finished with count: {task.Result}");
        }
    }
}

 

Action은 리턴값이 없는 메서드 전용이기 때문에 이 경우 Action을 사용할 수 없다.

 

순서는 약간 달라질 수 있다.

 

 

이번엔 인수도 있고 리턴값도 있는 스레드를 만들어 보자.

아래 2가지 코드는 모두 같은 내용이지만 다른 방식으로 작성되었다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static int Counter(object to)
        {
            int subCounter = 0;
            for (int i = 0; i < (int)to; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }

            return subCounter;
        }

        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Counter, 5);
            task.Start();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
            Console.WriteLine($"Sub Thread finished with count: {task.Result}");
        }
    }
}

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Func<object, int> Counter = (object to) =>
            {
                int subCounter = 0;
                for (int i = 0; i < (int)to; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }

                return subCounter;
            };

            Task<int> task = new Task<int>(Counter, 5);
            task.Start();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            task.Wait();
            Console.WriteLine($"Sub Thread finished with count: {task.Result}");
        }
    }
}

 

 

 

async, await를 사용해 보자.

 

아래 코드는 컴파일 에러가 발생한다. Main 함수에는 async가 단순하게 그냥 붙을 수 없다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
    	// 컴파일 에러 발생
        static async void Main(string[] args)
        {
            await Task.Run(() => Count());

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }
        }

        static void Count()
        {
            int subCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }

        }
    }
}

 

Main 함수에 단순하게 async가 붙으면 다음과 같은 에러가 발생한다.

Program does not contain a static 'Main' method suitable for an entry point

 

또, 아래와 같이 바꾸면 단일 스레드처럼 동작한다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            await Task.Run(() => Count());

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }
        }

        static void Count()
        {
            int subCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }

        }
    }
}

 

 

await 키워드는 이 비동기 작업(서브 스레드)이 완전히 끝날 때까지 여기서 기다리겠다는 의미이기 때문이다.

 

아래와 같이 바꿔보자.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            // await 없이 Task를 실행(시작)만 하고 변수에 담아둔다.
            // 이 시점부터 서브 스레드가 백그라운드에서 동작한다.
            Task countTask = Task.Run(() => Count());

            // 메인 스레드는 멈추지 않고 자신의 루프 작업을 동시에 수행한다.
            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                // 참고: 비동기 메서드(async) 내에서는 Thread.Sleep 대신 await Task.Delay를 사용하는 것을 권장.
                await Task.Delay(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            // 메인 스레드의 작업이 다 끝나면, 메인 프로세스가 종료되기 전에
            // 서브 스레드의 작업이 모두 완료되었는지 마지막으로 확인(대기).
            await countTask;
        }

        static void Count()
        {
            int subCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Sub Thread counting... {subCounter++}");
            }
        }
    }
}

 

아니면 아래와 같이 다른 함수에서 async, await를 사용하도록 코드를 바꾼다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Count();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }
        }

        static async void Count()
        {
            await Task.Run(async () =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    // Use Task.Delay instead of Thread.Sleep to avoid blocking the thread
                    await Task.Delay(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }
            });
        }
    }
}

 

 

 

이번엔 반환값이 있는 함수를 살펴보자.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> taskResult = Count();

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }

            int result = taskResult.Result;
            Console.WriteLine($"Result from Sub Thread: {result}");
        }

        static async Task<int> Count()
        {
            return await Task.Run(() =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }

                return subCounter;
            });
        }
    }
}

 

 

위 코드를 아래와 같이 바꾸면 단일 스레드처럼 작동하게 된다. 조심하자.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int taskResult = Count().Result;
            // Task가 시작되자마자 바로 Result를 요구하고 있다.
            // 메인 스레드가 진행하지 못하게 된다.
            Console.WriteLine($"Result from Sub Thread: {taskResult}");

            int mainCounter = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Main Thread counting... {mainCounter++}");
            }
        }

        static async Task<int> Count()
        {
            return await Task.Run(() =>
            {
                int subCounter = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"Sub Thread counting... {subCounter++}");
                }

                return subCounter;
            });
        }
    }
}

 

 

 

Parallel 클래스를 사용해서 간편하게 스레드를 사용해 보자.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Parallel.For(0, 10, DoSomething);

            // Alternatively, you can use a lambda expression:
            //Parallel.For(0, 10, i =>
            //{
            //    DoSomething(i);
            //});
        }

        static void DoSomething(int i)
        {
            Thread.Sleep(1000);
            Console.WriteLine($"Job {i} completed.");
        }
    }
}

 

 

하나씩 처리하면 10초가 걸리는 작업이 10개의 스레드가 생성되어 1초 만에 끝난다. (스레드는 CPU 코어의 갯수에 따라 다르게 생성된다)

 

반응형
Posted by J-sean
:
반응형

간단한 멀티 스레드와 뮤텍스 사용 예를 확인해 보자.

 

뮤텍스를 사용하지 않은 멀티스레드.

#include <iostream>
#include <thread>

void counter(int id, int max) {
	for (int i = 0; i < max; i++) {
		std::cout << "id: " << id << ", : " << i << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(1));	// 1초 대기
	}
}

int main() {
	std::thread t1(counter, 1, 10);
	std::thread t2(counter, 2, 10);

	t1.join();
	t2.join();

	return 0;
}

 

cout 출력 도중 컨텍스트 스위칭이 발생하며 출력이 엉망이 되어 버렸다.

 

뮤텍스를 사용한 멀티스레드.

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;

void counter(int id, int max) {
	for (int i = 0; i < max; i++) {
		m.lock();
		std::cout << "id: " << id << ", : " << i << std::endl;
		m.unlock();
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

int main() {
	std::thread t1(counter, 1, 10);
	std::thread t2(counter, 2, 10);

	t1.join();
	t2.join();

	return 0;
}

 

출력이 깔끔하다.

 

하지만 위 코드에 나오는 뮤텍스의 lock(), unlock()를 직접 호출하면 안 된다. 뮤텍스 락은 일종의 리소스이기 때문에 거의 대부분 RAII 원칙에 따라 독점적으로 획득된다. 뮤텍스에 락을 정확히 걸거나 해제하는 작업을 쉽게 처리할 수 있는 lock_guard, unique_lock 등의 락 클래스를 사용하자.

 

lock_guard를 사용한 뮤텍스 멀티 스레드.

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;

void counter(int id, int max) {
	for (int i = 0; i < max; i++) {
		{
			// 락이 필요한 블럭
			std::lock_guard lock(m);
			std::cout << "id: " << id << ", : " << i << std::endl;
		}
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

int main() {
	std::thread t1(counter, 1, 10);
	std::thread t2(counter, 2, 10);

	t1.join();
	t2.join();

	return 0;
}

 

깔끔하고 안전한 출력.

 

반응형
Posted by J-sean
:
반응형

x64dbg에서 디버깅 중 Pause 명령이 실행되지 않는 경우가 있다.

 

F12 키를 누르면 에러가 발생한다.

 

Threads 탭을 확인해 보자.

 

현재 실행되고 있는 Thread가 Main이 아닌, 7번 14328이기 때문이다. 나머지 Thread는 Suspended 되어 있다.

 

Main 스레드에서 우클릭 - Switch Thread를 선택한다.

 

Pause는 Main 스레드에서만 사용할 수 있다. Main 스레드로 바꾸면 Pause를 사용할 수 있다.

 

반응형
Posted by J-sean
:
반응형

스레드 클래스를 상속받는 클래스를 정의하고 사용해 보자.

 

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
import time
import threading
 
class Worker(threading.Thread):
    def __init__(self, name, count, delay):
        super().__init__()
        self.name = name
        self.count = count
        self.delay = delay
 
    # 스레드 클래스를 상속하는 클래스는 run()를 재정의 해야 한다.
    # 객체를 만들고 start()를 실행하면 run()가 실행된다.
    def run(self):
        print(f"{self.name} job started.")
        for i in range(self.count):
            print(f"{self.name} job: {i}.")
            time.sleep(self.delay)
        print(f"{self.name} job finished.")
 
            
print("Main started.")
 
thread_1 = Worker("First"50.5)
#thread_1.daemon = True
# 데몬 스레드로 설정되면 메인 스레드 종료시 서브 스레드도 종료된다.
thread_1.start()
#thread_1.join()
# join()을 실행한 스레드가 종료할 때까지 나머지 스레드는 대기한다.
 
thread_2 = Worker("Second"50.5)
#thread_2.daemon = True
thread_2.start()
#thread_2.join()
 
print(f"■ Number of threads: {threading.active_count()}")
 
time.sleep(1)
 
print("Main finished.")
 

 

 

 

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
import time
import threading
 
class Worker(threading.Thread):
    def __init__(self, name, count, delay):
        super().__init__()
        self.name = name
        self.count = count
        self.delay = delay
        
    def run(self):
        print(f"{self.name} job started.")
        for i in range(self.count):
            print(f"{self.name} job: {i}.")
            time.sleep(self.delay)
        print(f"{self.name} job finished.")
 
thread_1 = Worker("First"50.5)
thread_2 = Worker("Second"50.5)
thread_3 = Worker("Third"50.5)
threads = [thread_1, thread_2, thread_3]
 
print(f"■ Number of threads: {threading.active_count()}")
# 활성화된 스레드는 메인스레드 뿐이므로 1이 표시된다.
 
for thread in threads:
    thread.start()
    thread.join()
 

 

 

 

thread - Thread-based parallelism

 

반응형
Posted by J-sean
:
반응형

멀티 스레드를 사용해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time
import threading
 
def Job(name, count, delay):
    print(f"{name} job started.")
    for i in range(count):
        print(f"{name} job: {i}.")
        time.sleep(delay)
    print(f"{name} job finished.")
 
print("Main started.")
 
thread_1 = threading.Thread(target=Job, args=("First"50.5))
thread_1.start()
 
thread_2 = threading.Thread(target=Job, args=("Second"50.5))
thread_2.start()
 
print(f"■ Number of threads: {threading.active_count()}")
 
time.sleep(1)
 
print("Main finished.")
 

 

 

■ threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

- target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.
- args is a list or tuple of arguments for the target invocation. Defaults to ().

 

 

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
import time
import threading
 
def Job(name, count, delay):
    print(f"{name} job started.")
    for i in range(count):
        print(f"{name} job: {i}.")
        time.sleep(delay)
    print(f"{name} job finished.")
 
print("Main started.")
 
thread_1 = threading.Thread(target=Job, args=("First"50.5))
thread_1.daemon = True
thread_1.start()
 
thread_2 = threading.Thread(target=Job, args=("Second"50.5))
thread_2.daemon = True
thread_2.start()
 
print(f"■ Number of threads: {threading.active_count()}")
 
time.sleep(1)
 
print("Main finished.")
 

 

 

 

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
import time
import threading
 
def Job(name, count, delay):
    print(f"{name} job started.")
    for i in range(count):
        print(f"{name} job: {i}.")
        time.sleep(delay)
    print(f"{name} job finished.")
 
print("Main started.")
 
thread_1 = threading.Thread(target=Job, args=("First"50.5))
thread_1.start()
thread_1.join()
 
thread_2 = threading.Thread(target=Job, args=("Second"50.5))
thread_2.start()
thread_2.join()
 
print(f"■ Number of threads: {threading.active_count()}")
 
time.sleep(1)
 
print("Main finished.")
 

 

 

■ join(timeout=None)

- Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.
- When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.
- When the timeout argument is not present or None, the operation will block until the thread terminates.
- A thread can be joined many times.

 

 

반응형
Posted by J-sean
:
반응형

프로그램의 어떤 함수가 컴퓨터 CPU 자원을 독점하는지 찾아보자.

 

프로세스 익스플로러를 실행한다.

 

Options - Configure Symbols... 를 선택한다.

 

Dbghelp.dll 경로와 분석할 프로그램 Symbols 경로를 지정한다.

 

CPU 자원을 많이 소비하고 있는 프로세스를 찾는다.

 

 

우클릭 - Properties... 를 선택한다.

 

Threads 탭을 선택하고 CPU를 많이 사용하는 스레드를 찾아 선택하고 Stack 버튼을 클릭한다.

 

ThreadFunc2 함수가 실행되고 있음을 확인한다.

 

ThreadFunc2()

 

반응형
Posted by J-sean
:
반응형

작성 중....................

 

 

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
46
47
48
49
#include <stdio.h>
#include <Windows.h>
 
static int v1 = 0;
static int v2 = 0;
 
VOID WINAPI Tls_callback1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    if (Reason == DLL_PROCESS_ATTACH)
        v1 = 1;
}
VOID WINAPI Tls_callback2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    if (Reason == DLL_PROCESS_ATTACH)
        v2 = 2;
}
 
#pragma comment (linker, "/INCLUDE:__tls_used")
 
/**** Section 1. Start
TLS callback 함수 등록은 Section 1 이나 Section 2 스타일 모두 가능.
 
#pragma comment (linker, "/INCLUDE:_p_tls_callback1")
 
#pragma data_seg(push)
#pragma data_seg(".CRT$XLC")
EXTERN_C PIMAGE_TLS_CALLBACK p_tls_callback1 = Tls_callback1;
#pragma data_seg(".CRT$XLD")
EXTERN_C PIMAGE_TLS_CALLBACK p_tls_callback2 = Tls_callback2;
#pragma data_seg(pop)
 
Section 1. End ****/
 
/**** Section 2 Start ****/
 
#pragma data_seg(push)
#pragma data_seg(".CRT$XLC")
EXTERN_C PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { Tls_callback1, Tls_callback2, 0 };
#pragma data_seg()
#pragma data_seg(pop)
 
/**** Section 2 End ****/
 
int main() {
 
    printf("Test values from tls callbacks are: tls1 = %d, tls2 = %d\n", v1, v2);
 
    return 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
#include <stdio.h>
#include <Windows.h>
 
VOID WINAPI Tls_callback(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    if (Reason == DLL_PROCESS_ATTACH)
        if (IsDebuggerPresent())
        {
            MessageBoxA(NULL"Debugger present.""Detector", MB_OK);
            exit(-1);
        }
}
 
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:_p_tls_callback")
 
#pragma data_seg(push)
#pragma data_seg(".CRT$XLC")
EXTERN_C PIMAGE_TLS_CALLBACK p_tls_callback = Tls_callback;
#pragma data_seg(pop)
 
int main() {
 
    printf("No debugger present.");
 
    return 0;
}
 

 

 

 

 

 

 

참고

https://lallouslab.net/2017/05/30/using-cc-tls-callbacks-in-visual-studio-with-your-32-or-64bits-programs/

https://blog.naver.com/stop2y/221201916660

 

 

반응형
Posted by J-sean
:
반응형

Message, Handler, Looper를 이용해 메인 스레드에서 다른 스레드로 메세지(데이터)를 보내자.

 

레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    textView.setText(msg.obj + " from My Thread.");
                    // 메인 스레드의 메인 루퍼를 사용하기 때문에
                    // textView()에 바로 접근 가능하다.
                }
            };
        }
    }
}
 

 

메인 루퍼를 이용하는 소스를 작성하고 빌드한다.

 

에디트텍스트에 메세지를 입력하고 버튼을 터치하면 텍스트뷰에 표시된다.

 

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            handler = new Handler(Looper.myLooper()) {
                // 메인 스레드가 아닌 새로 만든 이 스레드의 루퍼 사용
                @Override
                public void handleMessage(@NonNull Message msg) {
                    String message = msg.obj.toString();
                    // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                    // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                    textView.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(message + " from My Thread");
                        }
                    });
                }
            };
 
            Looper.loop();
        }
    }
}
 

 

메인 루퍼가 아닌 새로 만든 스레드의 루퍼를 생성하고 메세지를 처리하는 소스. 결과는 동일하다.

 

 

(루퍼 종료용)버튼을 하나 더 추가한다.

 

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.myHandler.sendMessage(message);
            }
        });
 
        Button button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myThread.myHandler.StopLooper();
                // 새로 만든 스레드의 루퍼 종료.
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        MyHandler myHandler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            myHandler = new MyHandler();
 
            Looper.loop();
        }
 
        class MyHandler extends Handler {
            MyHandler() {
                super(Looper.myLooper());
            }
 
            @Override
            public void handleMessage(@NonNull Message msg) {
                String message = msg.obj.toString();
                // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                textView.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(message + " from My Thread");
                    }
                });
            }
 
            void StopLooper() {
                myHandler.getLooper().quitSafely();
                // 새로 만든 스레드의 루퍼 종료.
            }
        }
    }
}
 

 

새 스레드의 루퍼를 종료하는 함수가 포함된 파생 Handler 클래스를 사용하는 소스.

Looper.XXX() 호출 시 MyThread 클래스 내부라도 어디서 루퍼를 참조 하느냐에 따라 메인 루퍼가 참조 될 수도 있고 새 스레드의 루퍼가 참조 될 수 있다. (MyThread.Run()에서만 새 스레드의 루퍼가 참조된다)

 

스레드 루퍼 종료 버튼 터치 후 다시 스레드로 보내기 버튼을 터치하면 아래와 같은 경고 발생.

 

새 스레드의 루퍼가 종료(별 다른 작업이 없다면 동시에 새 스레드도 종료) 된 후 핸들링할 메세지를 보내기 때문에 경고가 발생한다. (예외 처리 필요)

 

※ 참고

Looper

 

반응형
Posted by J-sean
: