我有单线程进程,执行时间很长。我需要几个用户才能访问执行此过程,我选择http协议来管理调用。
当然,当一个过程正在运行时,其他人应该等到它完成。如果进程可用,则执行。如果没有,则发送BUSY应答。
这是实施:
using System;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace simplehttp
{
class Program
{
private static System.AsyncCallback task;
private static System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);// Notifies one or more waiting threads that an event has occurred.
private static HttpListenerContext workingContext = null;
public static bool isBackgroundWorking()
{
return mre.WaitOne(0);
}
static void Main(string[] args)
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (true)
{
Console.WriteLine(" waitOne " + isBackgroundWorking());
mre.WaitOne(); // Blocks the current thread until the current WaitHandle receives a signal.
Console.WriteLine(" do job" + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n");
HttpListenerRequest request = workingContext.Request;
HttpListenerResponse response = workingContext.Response;
string responseString = "WORK " + DateTime.Now ;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
Thread.Sleep(10000);
output.Write(buffer, 0, buffer.Length);
output.Close();
Console.WriteLine(" " + responseString + "\t" + DateTime.Now);
workingContext = null;
mre.Reset(); // Sets the state of the event to nonsignaled, causing threads to block.
}
}).Start();
// Create a listener.
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:6789/index/");
listener.Start();
Console.WriteLine("Listening..." + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n");
task = new AsyncCallback(ListenerCallback);
IAsyncResult resultM = listener.BeginGetContext(task,listener);
Console.WriteLine("Waiting for request to be processed asyncronously.");
Console.ReadKey();
Console.WriteLine("Request processed asyncronously.");
listener.Close();
}
private static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener) result.AsyncState;
//If not listening return immediately as this method is called one last time after Close()
if (!listener.IsListening)
return;
HttpListenerContext context = listener.EndGetContext(result);
listener.BeginGetContext(task, listener);
if (workingContext == null && !isBackgroundWorking())
{
// Background work
workingContext = context;
mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
}
else
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseString = "BUSY "+ DateTime.Now + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
Console.WriteLine(responseString + "\t" + DateTime.Now);
}
}
}
}
测试我做2次http调用。我希望有2个不同的答案WORK和BUSY。 但是我看到第二个请求先等待完成然后执行。
waitOne False
Listening... [:10 ]
Waiting for request to be processed asyncronously.
do job [:11 ]
WORK 1/24/2016 10:34:01 AM 1/24/2016 10:34:11 AM
waitOne False
do job [:11 ]
WORK 1/24/2016 10:34:11 AM 1/24/2016 10:34:21 AM
waitOne False
我理解它应该如何运作有什么问题?
更新(SO没有太多评论): 我的代码看起来很尴尬,因为它是真实进程的复制。在“我的”应用程序中,工作过程是主要过程,在某些特定时刻运行嵌入式C#代码具有“礼貌”。因此,我无法运行新任务来处理请求,并且它必须是异步的,因为工作流程完成自己的工作,并且只调用从属代码片段以在数据可用时通知客户端。它是异步的,因为代码被调用并且应该尽快完成,否则它将阻止主应用程序。 我将尝试使用同步调用添加其他线程,并查看它会影响情况。
此示例中未使用调试器来干扰打印到控制台的实时进程和时间戳。调试很棒且必要,但在这种情况下,我尝试用输出替换以避免同步/等待场景中的额外actor。
应用程序本身并不是重载对话。 1-3个客户很少向主要申请人询问答案。 http协议用于方便而不是繁重或经常对话。看起来像IE这样的浏览器工作正常(Windows到Windows会话?),有些像Chrome(更多系统不可知)复制我的应用程序行为。看看时间戳,Chrome,IE,IE,Chrome和最后一个Chrome仍然进入了WORK流程。顺便说一下,每个会话建议更改了代码,现在在检索上一个请求后立即发出新请求。
HttpListenerContext context = listener.EndGetContext(result);
listener.BeginGetContext(task, listener);
另外,根据建议,我改变了对syncroniuos的异步调用,结果仍然是相同的
private static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener) result.AsyncState;
//If not listening return immediately as this method is called one last time after Close()
if (!listener.IsListening)
return;
HttpListenerContext context = listener.EndGetContext(result);
while (true)
{
if (workingContext == null && !isBackgroundWorking())
{
// Background work
workingContext = context;
mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
}
else
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseString = "BUSY " + DateTime.Now + " [" + Thread.CurrentThread.Name + ":" +
Thread.CurrentThread.ManagedThreadId;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
Console.WriteLine(responseString + "\t" + DateTime.Now);
}
context=listener.GetContext();
}
}
答案 0 :(得分:4)
答案 1 :(得分:3)
我还没有完全阅读并理解该代码。它的结构很尴尬。这是一个更清洁的结构,很容易做对:
while (true) {
var ctx = GetContext();
Task.Run(() => ProcessRequest(ctx));
}
这只是发送所有传入的工作。然后:
object @lock = new object(); //is work being done?
void ProcessRequest(Context ctx) {
if (!Monitor.Enter(@lock))
SendBusy();
else {
ProcessInner(ctx); //actually do some work
Monitor.Exit(@lock);
}
}
这真的是必要的。
特别是使用异步IO是没有意义的。我假设你从某个地方复制了那个代码或想法。常见的错误。 Async IO在这里对你没有任何帮助,并使代码错综复杂。