지훈_한
7월 17, 2025, 5:12오전
1
안녕하세요.
아래와 같이 클래스 A에서 클래스 B의 비동기 함수를 try-catch로 감싸 호출했습니다.
클래스 B의 비동기 함수 내부에서 예외를 throw했는데,
예외가 try-catch에서 잡히지 않고 프로그램이 종료됩니다.
혹시 어떤 이유 때문인지, 그리고 어떻게 처리해야 하는지 조언 부탁드립니다.
감사합니다.
// 클래스B: 비동기 함수에서 예외 발생
public class B
{
public async Task DoWorkAsync()
{
await Task.Run(() =>
{
throw new InvalidOperationException("예외 발생!");
});
}
}
// 클래스A: 비동기 함수 호출 및 try-catch
public class A
{
private readonly B b = new B();
public async Task RunAsync()
{
try
{
await b.DoWorkAsync();
}
catch (Exception ex)
{
Console.WriteLine($"예외 잡음: {ex.Message}");
}
}
}
3개의 좋아요
var a = new A();
await a.RunAsync();
// 클래스B: 비동기 함수에서 예외 발생
public class B
{
public async Task DoWorkAsync()
{
await Task.Run(() =>
{
throw new InvalidOperationException("예외 발생!");
});
}
}
// 클래스A: 비동기 함수 호출 및 try-catch
public class A
{
private readonly B b = new B();
public async Task RunAsync()
{
try
{
await b.DoWorkAsync();
}
catch (Exception ex)
{
Console.WriteLine($"예외 잡음: {ex.Message}");
}
}
}
예외 발생이 잘 되는데… 샘플 프로젝트 만드셔서 전달 가능하실까요?
아, .NET 9 콘솔 프로젝트로 만든 것입니다.
1개의 좋아요
@Vincent 님께서 그제 올리신 질문과 비슷한 이유이지 않을까 싶은데요ㅎ
의문의 .NET 프로세스 종료. 디버깅 노하우를 구합니다. (MiniExcel) - 프로그래밍 언어 Q&A - 닷넷데브
await
없이 A.RunAsync()
를 호출하신게 아닐지ㅎ
// static void Main()
new A().RunAsync(); // 예외 안잡힘
// static async Task Main()
await new A().RunAsync() // 예외 잡힘
3개의 좋아요
지훈_한
7월 17, 2025, 5:44오전
5
저도 방금 콘솔 창에선 잘 되는 것을 확인했지만, Winform 환경의 프로젝트 코드에서는 자꾸 에러를 못잡고, throw 하는 부분에서 자꾸 프로그램이 죽습니다… 혹시 UI 스레드나 다른 문제때문에 이렇게 프로그램이 계속 죽는 것일까요?
public async Task OnInitializeAsync()
{
var filePath = _mainview.SelectedFilePath;
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
{
_mainview.ShowMessage("올바른 파일을 선택하세요.");
return;
}
// 싱글톤 인스턴스 가져오기
manager = Manager.Instance;
try
{
// 비동기 초기화 호출
await manager.InitializeAsync(filePath);
}
catch (Exception ex)
{
MessageBox.Show($"초기화 실패: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
LogMessage("Initialization");
}
public async Task InitializeAsync(string configFilePath)
{
// 백그라운드 스레드에서 초기화 수행
await Task.Run(() =>
{
resultCode |= SomeControlLibrary.InitializeFromFile(out handle, configFilePath);
if (resultCode != 0)
{
SomeControlLibrary.Delete(handle);
throw new InvalidOperationException("Initialization failed!");
}
initiator = new AutoStart(handle);
resultCode |= SomeControlLibrary.RegisterCallbackJobReady(handle, initiator);
monitor = new WaitForFinished();
resultCode |= SomeControlLibrary.RegisterCallbackJobFinished(handle, monitor);
});
}
지훈_한
7월 17, 2025, 5:49오전
6
분명 Catch로 잡고있는데 처리하지않은 예외라 뜨네요. 어떤 실수를 했을까요…?
tkm
7월 17, 2025, 5:50오전
7
예외처리의 MessageBox.Show
부분을
await this.InvokeAsync(() => {
MessageBox.Show($"초기화 실패: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
});
위처럼 바꿔보세요
await
밖에서 catch
하고 있는 경우 Task를 실행하고 있는 스레드의 호출 스택상으로는 catch
하고 있는 부분이 없는 것으로 간주되기 때문에 사용자가 처리하지 않은 예외
로 뜨는것은 정상입니다.
F5를 눌러 계속하면 죽나요?
1개의 좋아요
만약, 디버깅 모드 실행이고, 디버거가 예외를 보여주는 현상을 "프로그램이 종료"한다고 표현한 것이라면, [계속(F5)] 을 누르면 Catch 블럭으로 이동할 것이라 정상 동작입니다.
릴리스 모드 실행이라면 디버거 개입이 없으므로, 이러한 멈춤 현상이 없는 게 정상입니다. 그럼에도, 프로그램이 멈춘다면, 원인은 다른 곳에 있을 확률이 큽니다.
그리고, UI의 상태 변수를 다른 스레드와 공유하거나, UI 스레드에서 외부 변수를 직접 조작하면 그 스레드 혹은 의존 객체의 문제가 UI에게 전파되므로, 아래와 같이 격리하는 것이 비정상 종료를 예방하는데 도움이 됩니다.
public async IAsyncEnumerable<ResultCode> InitializeAsync(
string configFilePath,
[EnumeratorCancellation] CancellationToken ct)
{
var (code, handle) = await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
var code = SomeControlLibrary.InitializeFromFile(out var handle, configFilePath);
return (code, handle);
}, ct)
.ConfigureAwait(false); // 스레드 풀 스레드 사용
if (code != 0)
{
SomeControlLibrary.Delete(handle);
yield return ResultCode.InitializationFailed;
yield break;
}
yield return code;
yield return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
var initiator = new AutoStart(handle);
return SomeControlLibrary.RegisterCallbackJobReady(handle, initiator);
}, ct).ConfigureAwait(false);
yield return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
var monitor = new WaitForFinished();
return SomeControlLibrary.RegisterCallbackJobFinished(handle, monitor);
}, ct).ConfigureAwait(false);
}
소비 코드
public async Task OnInitializeAsync()
{
// ...
using var cts = new CancellationTokenSource();
EventHandler onCancel = (_, _) => cts.Cancel();
_initCancelBtnEnabled = true;
_initCancelBtn.Click += onCancel;
try
{
await foreach (var code in manager.InitializeAsync(filePath , cts.Token))
{
resultCode |= code;
if (code == ResultCode.InitializationFailed)
{
MessageBox.Show($"초기화 실패", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
LogMessage("Initialization");
}
catch(OperationCancelledException)
{
resultCode = ResultCode.Cancelled;
MessageBox.Show($"초기화 취소", "사용자", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
catch(// ...)
{
// 다른 예외 처리
}
finally
{
_initCancelBtnEnabled = false;
_initCancelBtn.Click -= onCancel;
}
}
주의 : 글쓰기 창에서 쓴 것이라, 오탈자 등의 이유로 동작하지 않을 수 있습니다.
현명_지
7월 19, 2025, 8:46오전
10
이렇게 해보세요…
public async Task SelectDatabase()
{
DataSet ds = new DataSet();
List listUserTemp = new List();
Exception exectpion = null;
Task t = Task.Run(() =>
{
try
{
using (SqlConnection sqlConnection = new SqlConnection(Properties.Settings.Default.ConnectionStr))
{
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
sqlDataAdapter.SelectCommand = new SqlCommand("SELECT * FROM USERINFO;",sqlConnection);
sqlDataAdapter.Fill(ds);
}
if (ds.Tables.Count!=0)
{
DataTable dt = ds.Tables[0];
for (int i = 0; i < dt.Rows.Count; i++)
{
USERINFO userInfo = new USERINFO();
userInfo.USERNAME = dt.Rows[i]["USERNAME"].ToString();
userInfo.USERIMG = dt.Rows[i]["USERIMG"].ToString();
userInfo.USERAGE = Int32.Parse(dt.Rows[i]["USERAGE"].ToString());
listUserTemp.Add(userInfo);
}
}
}
catch (Exception ex)
{
exectpion = ex;
}
});
await t;
if (exectpion!=null)
{
MessageBox.Show(exectpion.Message.ToString());
}
MyListUser = listUserTemp;
}