Я пытаюсь создать консольное приложение С#, которое будет отслеживать около 3000 URL-адресов (просто нужно знать, что HEAD-запрос возвратил 200, не обязательно содержимое и т.д.)
Моя попытка состояла в том, чтобы создать подпрограмму, проверяющую веб-URL, цикл и создание потоков, каждый из которых выполняет подпрограмму. Что происходит, если я запускаю с < 20 потоков, он выполняется нормально в большинстве случаев, но если я использую > 20 потоков, некоторые из тайм-аутов URL-адреса. Я попытался увеличить таймаут до 30 секунд, то же самое происходит. Сеть, в которой я запускаю эту операцию, более чем способна выполнять 50 запросов HTTP HEAD (соединение 10MBIT в ISP), и как процессор, так и сеть работают очень низко при выполнении подпрограммы.
Когда происходит тайм-аут, я тестирую один и тот же IP-адрес в браузере, и он работает нормально, я тестировал это несколько раз, и во время тестирования никогда не было случая, чтобы "тайм-аут" был фактически отключен.
Причина, по которой я хочу запустить > 20 потоков, - это то, что я хочу выполнить этот тест каждые 5 минут, причем часть URL-адреса занимает полный 10 секунд (или выше, если тайм-аут установлен выше), я хочу убедиться, что его можно запускать через все URL-адреса в течение 2-3 минут.
Есть ли лучший способ проверить, доступен ли URL-адрес, или я должен смотреть на систему/сеть на предмет проблемы.
while (rdr.Read())
{
Thread t = new Thread(new ParameterizedThreadStart(check_web));
t.Start(rdr[0]);
}
static void check_web(object weburl)
{
bool isok;
isok = ConnectionAvailable(weburl.ToString());
}
public static bool ConnectionAvailable(string strServer)
{
try
{
strServer = "http://" + strServer;
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
reqFP.Timeout = 10000;
reqFP.Method = "HEAD";
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
Console.WriteLine(strServer + " - OK");
rspFP.Close();
return true;
}
else
{
Console.WriteLine(strServer + " Server returned error..");
rspFP.Close();
return false;
}
}
catch (WebException x)
{
if (x.ToString().Contains("timed out"))
{
Console.WriteLine(strServer + " - Timed out");
}
else
{
Console.WriteLine(x.Message.ToString());
}
return false;
}
}
Просто помните, вы спросили.
Не создавайте такие потоки. Очень мало иметь больше потоков, чем процессорные ядра. Дополнительные потоки будут в значительной степени конкурировать друг с другом, тем более, что все они работают с одним и тем же кодом.
Вам нужно реализовать блоки с использованием. Если вы выбросите исключение (и, скорее всего, вы это сделаете), тогда вы будете терять ресурсы.
Какова цель возврата bool? Вы проверяете его где-то? В любом случае, ваша обработка ошибок и исключений - беспорядок.
Это не закончено, я не думаю, но попробую:
public static void Main()
{
// Don't mind the interpretation. I needed an excuse to define "rdr"
using (var conn = new SqlConnection())
{
conn.Open();
using (var cmd = new SqlCommand("SELECT Url FROM UrlsToCheck", conn))
{
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
// Use the thread pool. Please.
ThreadPool.QueueUserWorkItem(
delegate(object weburl)
{
// I invented a reason for you to return bool
if (!ConnectionAvailable(weburl.ToString()))
{
// Console would be getting pretty busy with all
// those threads
Debug.WriteLine(
String.Format(
"{0} was not available",
weburl));
}
},
rdr[0]);
}
}
}
}
}
public static bool ConnectionAvailable(string strServer)
{
try
{
strServer = "http://" + strServer;
var reqFp = (HttpWebRequest)WebRequest.Create(strServer);
reqFp.Timeout = 10000;
reqFp.Method = "HEAD";
// BTW, what an "FP"?
using (var rspFp = (HttpWebResponse) reqFp.GetResponse()) // IDisposable
{
if (HttpStatusCode.OK == rspFp.StatusCode)
{
Debug.WriteLine(string.Format("{0} - OK", strServer));
return true; // Dispose called when using is exited
}
// Include the error because it nice to know these things
Debug.WriteLine(String.Format(
"{0} Server returned error: {1}",
strServer, rspFp.StatusCode));
return false;
}
}
catch (WebException x)
{
// Don't tempt fate and don't let programs read human-readable messages
if (x.Status == WebExceptionStatus.Timeout)
{
Debug.WriteLine(string.Format("{0} - Timed out", strServer));
}
else
{
// The FULL exception, please
Debug.WriteLine(x.ToString());
}
return false;
}
}
public static void Main()
{
using (var conn = new SqlConnection())
{
conn.Open();
using (var cmd = new SqlCommand("", conn))
{
using (var rdr = cmd.ExecuteReader())
{
if (rdr == null)
{
return;
}
while (rdr.Read())
{
ThreadPool.QueueUserWorkItem(
CheckConnectionAvailable, rdr[0]);
}
}
}
}
}
private static void CheckConnectionAvailable(object weburl)
{
try
{
// If this works, it a lot simpler
var strServer = new Uri("http://" + weburl);
using (var client = new WebClient())
{
client.UploadDataCompleted += ClientOnUploadDataCompleted;
client.UploadDataAsync(
strServer, "HEAD", new byte[] {}, strServer);
}
}
catch (WebException x)
{
Debug.WriteLine(x);
}
}
private static void ClientOnUploadDataCompleted(
object sender, UploadDataCompletedEventArgs args)
{
if (args.Error == null)
{
Debug.WriteLine(string.Format("{0} - OK", args.UserState));
}
else
{
Debug.WriteLine(string.Format("{0} - Error", args.Error));
}
}
Не использовать потоки.
Асинхронные обратные вызовы и очереди. Зачем создавать поток, когда ресурс, который все они хотят, - это доступ к внешнему миру. Ограничьте свои потоки примерно до 5, а затем реализуйте класс, который использует очередь. разделите код на две части, выборку и процесс. Один управляет потоком данных, а другой контролирует доступ к внешнему миру.
Используйте любой язык, который вам нравится, но вы не ошибетесь, если считаете, что потоки предназначены для обработки, а хруст числа и асинхронные обратные вызовы предназначены для управления ресурсами.
Используйте класс ThreadPool
. Не создавайте сотни таких нитей. У потоков есть такие огромные накладные расходы, и в вашем случае происходит то, что ваш процессор будет тратить 99% времени на переключение контекста и 1% на выполнение реальной работы.