한빛 미디어에 나온 기사 스크랩입니다.
저자: Stephen Teilhet and Jay Hilyard, 번역 한동훈
원문 URL: http://www.ondotnet.com/lpt/a/4628
편집자 노트: 근간 C# Cookbook의 저자인 Stephen Teilhet과 Jay Hilyard은 독자 여러분이 책 속에서 볼 수 있는 다양한 해결책들의 일부를 보여주기 위해 ONDotNet에 직접 선별한 요리법(Recipe)들을 소개하고 있습니다. 오라일리의 쿡북 시리즈의 최근 출판물과 마찬가지로 여기 있는 해결책들은 문제의 핵심에 바로 접근합니다. 예를 들어, URL로 부터 원하는 HTML을 가져오기 위해 GetHTMLFromURL을 사용하는 방법을 설명합니다. 독자 여러분이 이전에 소개된 요리법들을 본 적이 없다면 저자들의 첫 번째 요리법을 여기서 찾아보시기 바랍니다.
요리법 13.8: URL에서 HTML 가져오기
문제
필요한 정보를 추려내기 위해 웹 서버에서 HTML을 가져와야 한다. 예를 들어, HTML에서 다른 페이지에 대한 링크나 뉴스 사이트의 헤드라인을 조사하고 싶습니다.
해결책
웹에서 통신하기 위해 요리법 13.5와 13.6에서 설정한 메서드를 사용하여 HTTP 요청을 만들고 응답을 검사합니다. 그리고 나서 HttpWebResponse 객체의 ResponseStream 속성을 사용하여 HTML을 가져옵니다.
public static string GetHTMLFromURL(string url)
{
if(url.Length == 0)
throw new ArgumentException("Invalid URL","url");
string html = "";
HttpWebRequest request = GenerateGetOrPostRequest(url,"GET",null);
HttpWebResponse response = (HttpWebResponse)request.GetResponse( );
try
{
if(VerifyResponse(response)== ResponseCategories.Success)
{
// get the response stream.
Stream responseStream = response.GetResponseStream( );
// use a stream reader that understands UTF8
StreamReader reader = new StreamReader(responseStream,Encoding.UTF8);
try
{
html = reader.ReadToEnd( );
}
finally
{
// close the reader
reader.Close( );
}
}
}
finally
{
response.Close( );
}
return html;
}
논의
GetHTMLFromURL 메서드는 웹 페이지를 가져오는 작업에 GenerateGetOrPostRequest와 GetResponse 메서드를 사용하고, 응답을 검사하는데 VerifyResponse 메서드를 사용합니다. 그 다음에 유효한 응답을 받으면 반환된 HTML에서 필요한 정보검색을 시작합니다.
HttpWebResponse 클래스의 GetResponseStream 메서드는 System.IO.Stream 객체가 반환하는 메시지의 본문 부분을 포함한 스트림을 반환한다. 데이터를 읽어오려면 StreamReader 객체를 응답 스트림으로 초기화하고, Encoding 클래스의 속성을 UTF8 속성을 사용하여 UTF8로 인코딩된 텍스트 데이터를 스트림에서 올바르게 읽어들이도록 합니다. StreamReader의 ReadToEnd를 호출하여 문자열 변수 html에 모든 컨텐트를 저장한 후 반환합니다.
참조
MSDN 도움말에서 HttpWebResponse.GetResponseStream 메서드, Stream 클래스, StringBuilder 클래스를 참고하세요.
요리법 15.8: 자원의 읽기 쓰기를 효과적으로 동기화하기
문제
스레드들이 공유하는 자원이 하나 있습니다. 스레드가 공유 자원에 쓰기 작업을 하고 있는 동안에는 다른 스레드들이 액세스를 해서는 안됩니다. 그러나 스레드들이 공유 자원에 대해서 읽기 작업을 하는 동안에는 이러한 작업에 대한 배타적인 액세스를 제공하지 않을 것입니다. 왜냐하면 이러한 액세스는 부하를 유발하기 때문입니다. 즉, 공유자원에 쓰기를 하는 동안에는 스레드 하나만 공유자원에 액세스할 수 있도록 하고 싶지만, 읽기는 스레드들이 동시에 수행할 수 있습니다. 스레드들이 리소스로부터 읽기를 하는 동안에는 읽기 작업이 끝날 때 까지 쓰기 작업이 발생하지 않게 해야 합니다.
해결책
FCL에서 ReaderWriterLock 클래스를 사용하라. ReaderWriterLock 클래스는 멀티 스레드 환경에서 비교적 덜 업데이트 되지만 보호가 필요한 경우와 같은 시나리오에 대해서 최적화되어 있다. 예를 들어, 강사가 수업에 참여한 학생들의 성적을 게시하는 게시판을 뜻하는 GradeBoard 클래스를 만들었다고 하자. 많은 학생들은 이 게시판을 읽을 수 있지만, 오직 강사만 게시판에 성적을 게시할 수 있다. 그러나 강사가 성적을 업데이트하고 있는 동안에 학생들은 읽기를 할 수 없어야 한다.
class GradeBoard
{
// make a static ReaderWriterLock to allow all student threads to check
// grades and the instructor thread to post grades
static ReaderWriterLock readerWriter = new ReaderWriterLock( );
// the grade to be posted
static char studentsGrade = ' ';
static void Main( )
{
// create students
Thread[] students = new Thread[5];
for(int i=0;i
students[i] = new Thread(new ThreadStart(StudentThreadProc));
students[i].Name = "Student " + i.ToString( );
// start the student looking for a grade
students[i].Start( );
}
// make those students "wait" for their grades by pausing the instructor
Thread.Sleep(5000);
// create instructor to post grade
Thread instructor = new Thread(new ThreadStart(InstructorThreadProc));
instructor.Name = "Instructor";
// start instructor
instructor.Start( );
// wait for instructor to finish
instructor.Join( );
// wait for students to get grades
for(int i=0;i
students[i].Join( );
}
}
static char ReadGrade( )
{
// wait ten seconds for the read lock
readerWriter.AcquireReaderLock(10000);
try
{
// now we can read safely
return studentsGrade;
}
finally
{
// Ensure that the lock is released.
readerWriter.ReleaseReaderLock( );
}
}
static void PostGrade(char grade)
{
// wait ten seconds for the write lock
readerWriter.AcquireWriterLock(10000);
try
{
// now we can post the grade safely
studentsGrade = grade;
Console.WriteLine("Posting Grade...");
}
finally
{
// Ensure that the lock is released.
readerWriter.ReleaseWriterLock( );
}
}
static void StudentThreadProc( )
{
bool isGradeFound = false;
char grade = ' ';
while(!isGradeFound)
{
grade = ReadGrade( );
if(grade != ' ')
{
isGradeFound = true;
Console.WriteLine("Student Found Grade...");
}
else // check back later
Thread.Sleep(1000);
}
}
static void InstructorThreadProc( )
{
// everyone likes an easy grader :)
PostGrade('A');
}
}
논의
예제에서, ReaderWriterLock 클래스는 GradeBoard 클래스의 학점 자원에 대한 액세스를 보호하는 역할을 한다. 많은 학생들은 ReadGrade 메서드를 사용해서 끊임없이 자신의 학점을 읽을 수 있지만, 강사만 PostGrade 메서드를 사용하여 학점을 게시할 수 있다. 학점 자원은 강사만 액세스할 수 있도록 잠겨있다. 강사는 학점을 업데이트하고 잠금(lock)을 해제한다. 대기중인 학생들은 요청을 다시 읽는 것을 허용한다. 모든 학생들은 계속해서 학점 게시판을 읽으며, 학점이 게시되었는지를 확인한다. 그리고 나서 다른 요청이 있을 때 까지 대기한다. 일단 학점이 게시되면, 각 학생들은 자신의 학점을 찾아볼 수 있으며, 학생 처리를 위한 스레드는 종료된다.
Main 메서드는 강사와 학생 스레드에 대해서 Join을 호출하여 스레드가 작업을 끝낸 후 다음 처리를 진행하고 종료할 때까지 기다린다. 만약, 이와 같이 동작하지 않는다면 프로그램은 스레드가 종료되기 전에 먼저 종료될 수도 있다. 이와 같은 동작은 ThreadInterruptedException이 발생하는 것을 보호한다. 만약 스레드가 종료된다면 Join 요청들은 이 예외를 던질 것이다. 디버깅을 쉽게 하기 위해 스레드의 Name 속성을 사용하여 이름을 부여하였다.
참조
MSDN 도움말에서 "ReaderWriterLock 클래스"와 "Thread 클래스"를 참조하세요.
'programming > c#' 카테고리의 다른 글
[펌] Threading in C# and .NET (0) | 2004.10.01 |
---|---|
[펌]Using Application Configuration Files in .NET (0) | 2004.09.03 |
.Net API Class의 Mock Object 만들기... (0) | 2004.09.03 |