c# - 从 Web API (2.1) 控制器运行 STA(单线程单元)线程
问题描述
我正在尝试从 Web API(2.1)控制器运行 STA(单线程单元)线程。
为此,我正在使用StaTaskScheduler:
/// <summary>Provides a scheduler that uses STA threads.</summary>
public sealed class StaTaskScheduler : TaskScheduler, IDisposable
{
/// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary>
private BlockingCollection<Task> _tasks;
/// <summary>The STA threads used by the scheduler.</summary>
private readonly List<Thread> _threads;
/// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary>
/// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param>
public StaTaskScheduler(int numberOfThreads)
{
// Validate arguments
if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel");
// Initialize the tasks collection
_tasks = new BlockingCollection<Task>();
// Create the threads to be used by this scheduler
_threads = Enumerable.Range(0, numberOfThreads).Select(i =>
{
var thread = new Thread(() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (var t in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(t);
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
// Start all of the threads
_threads.ForEach(t => t.Start());
}
}
一种选择是从以下位置运行 STA 线程CustomHttpControllerDispatcher
:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
//constraints: null,
// - Trying to avoid this
//handler: new Controllers.CustomHttpControllerDispatcher(config)
);
}
public class CustomHttpControllerDispatcher : System.Web.Http.Dispatcher.HttpControllerDispatcher
{
public CustomHttpControllerDispatcher(HttpConfiguration configuration) : base(configuration)
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// My stuff here
}
}
但是,这需要手动构建 http 响应,这涉及到 json 序列化,这是我宁愿避免的。
这给我留下了我当前的选项,即从控制器调用的库中运行 STA 线程(以生成自定义表单)。
如果使用Main
带有[STAThread]
. 但是,当从 Web 服务控制器调用时,当以下任务返回时会出现“吞咽”异常:
注意:我没有呈现任何对话框,因此不应出现任何“当应用程序未在 UserInteractive 模式下运行时显示模式对话框或表单不是有效操作”。异常,在从CustomHttpControllerDispatcher
.
private Task<AClass> SendAsync1()
{
var staTaskScheduler = new StaTaskScheduler(1);
return Task.Factory.StartNew<CustomForm>(() =>
{
// - execution causes "swallowed exception"
return new AClass();
},
CancellationToken.None,
TaskCreationOptions.None,
staTaskScheduler
);
}
也就是单步调试的时候,单步调试后堆栈跟踪消失:
return new AClass();
我通常通过缩小一些断点来解决这个问题,但对于这种情况,我认为这是不可能的,因为 Task.cs(以及大量相关文件)没有调试符号或源代码。
注意:我现在可以逐步进行System.Threading.Tasks
.
我怀疑可能是 b/c 我试图从 MTA 线程(控制器)安排一个 STA 线程,但不是肯定的?
用法
GetCustomForm().Wait();
private FixedDocumentSequence _sequence;
private async Task GetCustomForm()
{
// - will be slapped with a "calling thread cannot access...", next issue
_sequence = await SendAsync1b();
}
private readonly StaTaskScheduler _staTaskScheduler = new StaTaskScheduler(1);
private Task<FixedDocumentSequence> SendAsync1b()
{
//var staTaskScheduler = new StaTaskScheduler(100);
//var staTaskScheduler = new StaTaskScheduler(1);
return Task.Factory.StartNew<FixedDocumentSequence>(() =>
{
FixedDocumentSequence sequence;
CustomForm view = new CustomForm();
view.ViewModel = new ComplaintCustomFormViewModel(BuildingEntity.form_id, (int)Record);
sequence = view.ViewModel.XpsDocument.GetFixedDocumentSequence();
return sequence;
},
CancellationToken.None,
TaskCreationOptions.None,
_staTaskScheduler);
}
参考
- ASP.Net WebApi STA 模式
- ASP.Net WebAPI 中的自定义路由处理程序
- https://www.c-sharpcorner.com/article/global-and-per-route-message-handlers-in-webapi/
- https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers
- https://weblog.west-wind.com/posts/2012/Sep/18/Creating-STA-COM-compatible-ASPNET-Applications
解决方案
直接在 STA 线程中运行 WPF/Entity 代码似乎可以正常工作:
Thread thread = GetCustomFormPreviewView4();
thread.Start();
thread.Join();
private Thread GetCustomFormPreviewView4()
{
var thread = new Thread(() =>
{
FixedDocumentSequence sequence;
CustomForm view = new CustomForm();
view.ViewModel = new ComplaintCustomFormViewModel(BuildingEntity.form_id, (int)Record);
sequence = view.ViewModel.XpsDocument.GetFixedDocumentSequence();
//view.ShowDialog();
//...
}
);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}
推荐阅读
- python - 如何打印一个短语 n 次?
- gradle - 我们可以将代码库中的 Gradle 插件作为 JAR 文件提供吗
- python - 网站的 Django url 未指向正确的页面
- c - 包含 stdio 后 C 程序看不到 snprintf
- mysql - 修复 json 数据意外双重编码
- android - Flutter Erorr 找不到方法 android() 用于 org.gradle.api.Project 类型的项目“:app”的参数
- anaconda - 无法在 Anaconda Navigator 中安装 Python 包
- r - 如何对 R 中的列中的值进行分组或分类?
- ionic-framework - 我如何控制 IONIC 中的呼叫弹出选项
- .net - 如何立即使用选项(通过 services.Configure<> 映射)获取我的类的对象?