'thread'에 해당되는 글 5건
- 2008/12/30 MultiThread 예제 분석
- 2007/09/14 멀티 스레드 안전하게 구현하는 방법 - 데이터 동기화...
- 2007/09/12 스레드 사용한 이미지 뷰어 프로그램 - 데모.
- 2007/09/12 멀티 스레드.. 정리..
- 2007/09/12 멀티 스레드.. 동영상 강좌. 좋은것.. ^^
/* file Main.cpp
*
* This program is an adaptation of the code Rex Jaeschke showed in
* Listing 4 of his Nov 2005 C/C++ User's Journal article entitled
* "C++/CLI Threading: Part II". I changed it from C++/CLI (managed)
* code to standard C++.
*
* One hassle is the fact that C++ must employ a free (C) function
* or a static class member function as the thread entry function.
*
* This program must be compiled with a multi-threaded C run-time
* (/MT for LIBCMT.LIB or /MTd for LIBCMTD.LIB).
*
* John Kopplin 7/2006
*/
#include <stdio.h>
#include <windows.h> // for HANDLE
#include <process.h> // for _beginthread()
static bool interlocked = false; // change this to fix the problem
const int maxCount = 100000000;
static LONG value = 0; // under Windows Server 2003 you
// could use LONGLONG here
unsigned __stdcall TMain(void* arg)
{
if ( interlocked )
{
for ( int i = 1; i <= maxCount; i++ )
{
InterlockedIncrement(&value); // under Windows Server 2003 you
// could use InterlockedIncrement64() here
}
}
else
{
for ( int i = 1; i <= maxCount; i++ )
{
++value;
}
}
return 3; // thread exit code
}
int main()
{
// In this program we create 3 threads and request that their
// entry-point-function be the TMain() function which is a
// free (C) function and hence causes no problems for
// _beginthreadex()
HANDLE hth1;
unsigned uiThread1ID;
(원형) unsigned long _beginthread( void (*lpThreadEntryPoint)(void* lpArgList), unsigned uStackSize, void* lpArgList );
lpThreadEntryPoint 는 스레드가 시작된 함수의 주소인데, 이 함수에는 CreateThread()와 같이 한 개의 32비트 인자인 lpArgList가 있으며 이 값을 _beginthread()에 전달해야 한다. 이 함수는 void 타입으로 정의되어 CreateThread()와 조금 차이가 있다. 이 함수는 종료시까지 실행되는데 값을 반환하지는 않는다. uStackSize는 CreateThread()에서와 동일한 의미를 가지는데 스레드 스택의 베이스에서 커멧될 바이트 수이며, 이 값을 0으로 하면 윈도우는 부모 스레드에서 커멧된 것과 동일한 양의 메모리를 커멧힌다.
_beginthreadex() 는 CreateThread()와 완전히 같고 SECURITY_ATTRIBUTES 포인터와 시작 플래그(0이나 CREATE_SUSPENDED), 스레드 ID를 받는 포인터를 인자로 받는다. 일시 정지된 상태에서 스레드를 시작하거나 PostThreadMessage()에 스레드 ID를 사용하려면 _beginthreadex()를 사용해야 한다. _beginthread()
로 시작한 스레드만이 종료 코드를 설정할 수 있으므로 이때에도 필요하다.
hth1 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread1ID );
if ( hth1 == 0 )
printf("Failed to create thread 1\n");
DWORD dwExitCode;
스레드에서 return 문이 등장하면 윈도우는 ExitThread()를 대신 호출해서 return 문에 전달된 값을 전달해 주는데, 이 때문에 입구 함수는 DWORD의 반환값을 갖는다.
스레드를 시작하는 방법이 여러가지 있고 각각의 종료 함수도 다르므로 스레드에서는 단순히 return문을 사용하는 것이 좋다.
GetExitCodeThread( hth1, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 1 exit code = %u\n", dwExitCode );
HANDLE hth2;
unsigned uiThread2ID;
hth2 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread2ID );
if ( hth2 == 0 )
printf("Failed to create thread 2\n");
GetExitCodeThread( hth2, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 2 exit code = %u\n", dwExitCode );
HANDLE hth3;
unsigned uiThread3ID;
hth3 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread3ID );
if ( hth3 == 0 )
printf("Failed to create thread 3\n");
GetExitCodeThread( hth3, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 3 exit code = %u\n", dwExitCode );
// If we hadn't specified CREATE_SUSPENDED in the call to _beginthreadex()
// we wouldn't now need to call ResumeThread().
ResumeThread( hth1 ); // Jaeschke's // t1->Start();
ResumeThread( hth2 ); // Jaeschke's // t2->Start();
ResumeThread( hth3 ); // Jaeschke's // t3->Start();
// In C++ the process terminates when the primary thread exits
// and when the process terminates all its threads are then terminated.
// Hence if you comment out the following waits, the non-primary
// threads will never get a chance to run.
WaitForSingleObject( hth1, INFINITE ); // Jaeschke's t1->Join()
WaitForSingleObject( hth2, INFINITE ); // Jaeschke's t2->Join()
WaitForSingleObject( hth3, INFINITE ); // Jaeschke's t3->Join()
GetExitCodeThread( hth1, &dwExitCode );
printf( "thread 1 exited with code %u\n", dwExitCode );
GetExitCodeThread( hth2, &dwExitCode );
printf( "thread 2 exited with code %u\n", dwExitCode );
GetExitCodeThread( hth3, &dwExitCode );
printf( "thread 3 exited with code %u\n", dwExitCode );
printf( "After %d operations, value = %d\n", 3 * maxCount, value );
// under Windows Server 2003 you
// could use %I64d
// The handle returned by _beginthreadex() has to be closed
// by the caller of _beginthreadex().
CloseHandle( hth1 );
CloseHandle( hth2 );
CloseHandle( hth3 );
printf("Primary thread terminating.\n");
}
Thread를 두개를 사용할 경우 하나의 함수를 호출한다고 가정해 보자.
그렇게 되면 다량의 데이터를 효과적으로 여러개의 스레드를 사용해서 처리할 수가 있다.
아래의 예제를 보면 AddItems()를 스레드 두개를 사용해서 처리 되고 있다.
하지만 결과를 보면 100개의 데이터 값만 뿌려지는 것을 알수 있다.
이때 사용하게 되는 것이 lock 구문이다. 이 구문을 사용해서 멀티 스레드에서의
데이터를 안전하게 처리할 수 있다.
lock 에 포함되는 것은 동기화에 처리되어야 할 데이터를 락을 걸어 주게 되는 것으로
여겨진다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;namespace ConsoleApplication4
{
class Program
{
static List<string> list = new List<string>();static void Main(string[] args)
{
new Thread(AddItems).Start();
new Thread(AddItems).Start();
}static void AddItems()
{
for (int i = 0; i < 100; i++)
lock (list)
{
string _str = "Item " + list.Count + " ID=" + AppDomain.GetCurrentThreadId();
list.Add(_str);
Console.WriteLine(_str);
}
}
}
}
PathInfo.cs
----------------------------
using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
namespace GsiImageView
{
public class PathInfo
{
private string _path;
private string _filename;
public string Path
{
get { return _path; }
set { _path = value; }
}
public string FileName
{
get { return _filename; }
set { _filename = value; }
}
public PathInfo(string path, string filename)
{
this._path = path;
this._filename = filename;
}
}
public class PathInfoList : ObservableCollection<PathInfo>
{
public PathInfoList()
{
//최초 입력
Add(new PathInfo(@"D:\WPF_Project\GsiImageView\Image\1111.jpg", "1111.jpg"));
Add(new PathInfo(@"D:\WPF_Project\GsiImageView\Image\2222.jpg", "2222.jpg"));
}
}
}
Window1.xaml
-------------------------------
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:GsiImageView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:GsiImageView="clr-namespace:GsiImageView"
x:Class="GsiImageView.Window1"
x:Name="Window"
Title="Gsi ImageViewer"
Width="640" Height="480">
<Window.Resources>
<c:PathInfoList x:Key="pathData" />
<ObjectDataProvider x:Key="PathInfoListDS" d:IsDataSource="True" ObjectType="{x:Type GsiImageView:PathInfoList}"/>
<DataTemplate x:Key="PathInfoListTemplate">
<DataTemplate.Resources>
<Storyboard x:Key="OnLoaded1">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="stackPanel" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<StackPanel HorizontalAlignment="Stretch" Margin="0,2,2,2" x:Name="stackPanel" VerticalAlignment="Stretch" RenderTransformOrigin="0.5,0.5">
<StackPanel.Background>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FFA1A1A1" Offset="0"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Background>
<StackPanel.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</StackPanel.RenderTransform>
<TextBlock HorizontalAlignment="Stretch" Margin="0,2,0,2" VerticalAlignment="Stretch" Padding="4,0,0,0" Text="{Binding Path=FileName}"/>
<Border Height="Auto" BorderBrush="#FFCFD6FF" BorderThickness="2,2,2,2" Width="{Binding ElementName=Slider_ImageSize, Path=Value}">
<Image Width="Auto" Height="Auto" Source="{Binding Path=Path}"/>
</Border>
</StackPanel>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded" SourceName="stackPanel">
<BeginStoryboard Storyboard="{StaticResource OnLoaded1}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel/>
</ItemsPanelTemplate>
</Window.Resources>
<Window.Background>
<LinearGradientBrush EndPoint="0.988,0.988" StartPoint="0.008,0.054">
<GradientStop Color="#FFB5B5B5" Offset="0"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Window.Background>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="30.192"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label HorizontalContentAlignment="Center" Padding="0,0,0,0" VerticalContentAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Width="43" Content="경로 :"/>
<TextBox Background="{x:Null}" BorderBrush="#7F939393" BorderThickness="2,2,2,2" HorizontalContentAlignment="Left" Padding="0,1,0,1" VerticalContentAlignment="Center" Margin="43,0,0,0" x:Name="DirBox" Text="TextBox" TextWrapping="Wrap" HorizontalAlignment="Left" Width="59"/>
<Button HorizontalAlignment="Right" Margin="0,0,0,0" x:Name="FolderOpen" Width="63" Content="테스트" Click="OnFolderOpenClick"/>
<ListBox IsSynchronizedWithCurrentItem="True" Background="{x:Null}" Margin="4,4,4,4" x:Name="ImgListBox" Grid.Row="1" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemTemplate="{DynamicResource PathInfoListTemplate}" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" ItemsSource="{Binding Mode=OneWay, Source={StaticResource pathData}}"/>
<Slider Margin="109,0,70,0" x:Name="Slider_ImageSize" VerticalAlignment="Center" Maximum="1024" Minimum="10" Value="100"/>
</Grid>
</Window>
Window1.xaml.cs
-----------------------------
using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Threading;
namespace GsiImageView
{
public partial class Window1
{
public delegate void UpdateImage(string _path);
public Window1()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
//ObjectDataProvider dp = (ObjectDataProvider)this.FindResource("PathInfoListDS");
//pathInfolist = (PathInfoList)dp.Data;
}
private void OnFolderOpenClick(object sender, RoutedEventArgs e)
{
//ImgListBox.Items.Add(new PathInfo(@"D:\WPF_Project\GsiImageView\Image\3333.jpg", "3333.jpg"));
//Window1 w1 = new Window1();
Thread loadThread = new Thread(new ThreadStart(DataLoad));
loadThread.Start();
Thread.Sleep(1);
}
private void DataLoad()
{
//디렉토리 경로 가져 오기
DirectoryInfo di = new DirectoryInfo(@"D:\WPF_Project\GsiImageView\Image\MaxImageTest");
try
{
if (di.Exists)
{
//경로가 있음
foreach (FileInfo fi in di.GetFiles())
{
//경로를 타고 세부 이미지 이름을 얻어 온다.
string path = di.FullName + "\\" + fi.Name;
//il.Add(new PathInfo(path, fi.Name));
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.DataBind, new UpdateImage(UpdateImageList), path);
Thread.Sleep(60);
}
}
else
{
MessageBox.Show("경로가 확실하지 않습니다.");
return;
}
}
catch
{
MessageBox.Show("경로가 확실하지 않거나, 비정상 오류 입니다.");
return;
}
}
public void UpdateImageList(string _path)
{
PathInfoList pathinfolist = (PathInfoList)this.FindResource("pathData");
pathinfolist.Add(new PathInfo(_path, "none.jpg"));
// Thread.Sleep(1);
/*
//pathInfolist.Add(new PathInfo(_path, "none"));
ImgListBox.Items.Add(new PathInfo(_path, "none"));
ImgListBox.UpdateLayout();
try
{
int iii = 0;
}
catch(Exception e)
{
int iii = 0;
}
*/
}
}
}
메세지큐에 모든 동작 상태들을 담게 된다.
그리고 그 내용을 메인 스레드에서 실제로 작업 해도 담당하게 된다.
하지만 여기서 다른 스레드와의 동기 여부가 문제가 되며,
다른 스레드에서는 메인스레드에서 생성된 인스턴스된 객체들에 바로 제어를 하지 못한다.
그래서 다른 스레드에서 하는 작업을 메세지 큐에 집어 넣고 메인 스레드가 작업은 해야 한다.
그렇기 때문에 여기서 나오는 내용들이
Invoke, BeginInvoke 가 나오게 된다.
Invoke 해당 내용이 업데이트 될때까지 기다렸다가 다음 행을 시작하게 되며,
BeginInvoke 비동기 적으로 바로 다음 행으로 진행이 되며, 나중에 업데이트가 되게 된다.
...
메인 스레드에게 이러한 작업을 실행해라 하고 다른 스레드에서 위임을 시켜 줘야 한다.
//핸들러 설정
public delegate void UpdateUIHandler(DataSet _dsTmp);
public void GetTable()
{
Console.Write(...);
string SQLCommentText = ...
....
Thread.Sleep(1000 * 5);
//TargetGrid가 태어난 스레드에서 실행을 하게 된다.
TargetGrid.Invoke(new UpdateUIHandler(UpdateUI), new object[] {_dsTmp});
}
public void UpdateUI(DataSet _dsTmp)
{
TargetGrid.DataSource = _dsTmp.Tables[0];
}
실행을 하게 되면
내가 다르게 만든 스레드에서 UpdateUI를 하게 되지만 UpdateUI는 메인 스레드에서
작업 하게 된다.
윈도우 어플리케이션에서는 스레드를 사용하지만 다른 스레드에서 사용하지 않고
메인 스레드로 위임 해서 꼭 사용해야 한다.
이럴때 사용하는게 Invoke()가 된다.
BeginInvoke를 Invoke를 사용하게 되면
다음 행은 바로 실행 되고, UpdateUI를 실행하는 스레드는 따로 진행하게 된다.

Prev
Rss Feed