在C程序中,我使用PTHREAD_CANCEL_ASYNCHRONOUS立即取消线程,只要从父线程触发pthread_cancel。但是,它导致整个过程与分割故障崩溃。子线程的任务是从数据库服务器获取一些数据。我的逻辑是,如果它不能在10秒内得到数据,那么线程就应该从父线程中被杀死。
我只想杀死子线程,而不是整个进程。
struct str_thrd_data
{
SQLHANDLE hstmt;
int rc;
bool thrd_completed_flag;
};
void * str_in_thread_call(void *in_str_arg)
{
int thrd_rc;
struct str_thrd_data *str_arg;
str_arg = in_str_arg;
thrd_rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (thrd_rc != 0)
handle_error_en(thrd_rc, "pthread_setcancelstate");
thrd_rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (thrd_rc != 0)
handle_error_en(thrd_rc, "pthread_setcancelstate");
thrd_rc = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
if (thrd_rc != 0)
handle_error_en(thrd_rc, "pthread_setcanceltype");
// Code to call SQL Dynamic Query from a Database Server. This takes time more than 10 seconds.
thrd_rc = SQLExecute(hstmt);
printf("\n*********************Normal Thread termination withing timelimit %d\n",str_arg->rc);
str_arg->thrd_completed_flag = true;
}
int main()
{
printf("\nPJH: New THread created.\n");
pthread_attr_t tattr;
pthread_t th;
size_t mysize = 1;
struct str_thrd_data atd;
atd.hstmt = hstmt;
atd.rc= rc;
atd.thrd_completed_flag = false;
thrd_rc = pthread_attr_init(&tattr);
thrd_rc = pthread_attr_setstacksize(&tattr, mysize);
thrd_rc = pthread_create(&th, &tattr, &str_in_thread_call, &atd);
if (thrd_rc != 0)
handle_error_en(thrd_rc, "pthread_create");
// While Loop tp count till 10 seconds.
while(timeout !=0)
{
printf("%d Value of rc=%d\n",timeout, atd.rc);
if(atd.rc != 999) break;
timeout--;
usleep(10000);
}
rc = atd.rc;
//Condition to check if thread is completed or not yet.
if(atd.thrd_completed_flag == false)
{
//Thread not comepleted within time, so Kill it now.
printf("PJH ------- 10 Seconds Over\n");
thrd_rc = pthread_cancel(th);
printf("PJH ------- Thread Cancelled Immediately \n");
if (thrd_rc != 0)
{
handle_error_en(thrd_rc, "pthread_cancel");
}
printf("\nPJH &&&&&&&& Thread Cancelled Manually\n");
}
thrd_rc = pthread_join(th,NULL);
// some other job .....
}gdb process_name corefile显示了下面的回溯跟踪:-大部分是所有SQL函数。
#0 0xffffe410 in __kernel_vsyscall ()
#1 0x0059fe30 in raise () from /lib/libc.so.6
#2 0x005a1741 in abort () from /lib/libc.so.6
#3 0xdef3f5d7 in ?? () from /usr/lib/libstdc++.so.5
#4 0xdef3f624 in std::terminate() () from /usr/lib/libstdc++.so.5
#5 0xdef3f44c in __gxx_personality_v0 () from /usr/lib/libstdc++.so.5
#6 0x007e1917 in ?? () from /lib/libgcc_s.so.1
#7 0x007e1c70 in _Unwind_ForcedUnwind () from /lib/libgcc_s.so.1
#8 0x007cda46 in _Unwind_ForcedUnwind () from /lib/libpthread.so.0
#9 0x007cb471 in __pthread_unwind () from /lib/libpthread.so.0
#10 0x007c347a in sigcancel_handler () from /lib/libpthread.so.0
#11 <signal handler called>
#12 0xffffe410 in __kernel_vsyscall ()
#13 0x0064decb in semop () from /lib/libc.so.6
#14 0xe0245901 in sqloSSemP () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#15 0xe01e7f3c in sqlccipcrecv(sqlcc_comhandle*, sqlcc_cond*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#16 0xe03fe135 in sqlccrecv () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#17 0xe02a0307 in sqljcReceive(sqljCmnMgr*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#18 0xe02d0ba3 in sqljrReceive(sqljrDrdaArCb*, db2UCinterface*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#19 0xe02c510d in sqljrDrdaArExecute(db2UCinterface*, UCstpInfo*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#20 0xe01392bc in CLI_sqlCallProcedure(CLI_STATEMENTINFO*, CLI_ERRORHEADERINFO*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#21 0xe00589c7 in SQLExecute2(CLI_STATEMENTINFO*, CLI_ERRORHEADERINFO*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#22 0xe0050fc9 in SQLExecute () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1
#23 0x080a81f7 in apcd_in_thread_call (in_apcd_arg=0xbc8e8f34) at dcs_db2_execute.c:357
#24 0x007c4912 in start_thread () from /lib/libpthread.so.0
#25 0x0064c60e in clone () from /lib/libc.so.6发布于 2013-04-29 14:04:24
异步线程取消只能安全地使用在执行一组非常有限的操作的线程上--官方规则是长而混乱的,但实际上,受异步取消约束的线程只能执行纯计算。它们不能执行I/O操作,不能分配内存,不能获取任何类型的锁,也不能调用任何可能执行上述操作的库函数。将异步取消应用于与数据库对话的线程是不安全的。
延期取消是较少限制,但仍然是极端挑剔。如果您的数据库库没有编码以处理调用线程可能被取消的可能性,那么您也不能安全地使用延迟取消。
您将需要找到其他机制来中止运行时间过长的查询。
编辑:,因为这是DB2和名称混乱的"CLI“API,所以尝试使用SqlSetStmtAttr在准备好的语句上设置SQL_ATTR_QUERY_TIMEOUT参数。这是可以以这种方式设置的全部参数列表。和下面是对查询超时的更多讨论。
编辑的儿子:据一位比我做了更多数据库工作的朋友说,很可能有一种服务器端机制来取消慢速查询,而不管它们的来源如何。如果在DB2中存在这种情况,它可能比手动设置所有查询客户端的超时更方便,特别是因为它可能能够记录慢速查询,从而知道它们是哪些,并且可以对它们进行优化。
发布于 2013-04-29 14:12:07
由于数据库客户端代码的编写方式可能无法处理取消(大多数库代码不是这样),所以我认为这种方法是行不通的。详情见Zack的回答。
如果您需要能够取消数据库连接,则可能必须代理该连接并终止该代理。基本上,您要做的是创建第二个线程,该线程侦听端口并将连接转发到数据库服务器,并指示数据库客户端在本地主机上连接到此端口,而不是真正的数据库服务器/端口。然后,代理线程可以被取消(正常延迟取消,而不是异步取消),使用取消清理处理程序关闭套接字。通过关闭的套接字(而不仅仅是无响应的套接字)与数据库服务器失去连接,应该会导致数据库客户端库代码返回错误,然后您也可以让它的线程退出。
请记住,当设置这样一个代理时,您将需要确保您不会在访问数据库时引入安全问题。
下面是您可以用于代理的代码的草图,没有任何错误检查逻辑,也没有任何非预期客户端连接的说明:
int s, c;
struct addrinfo *ai;
struct sockaddr_in sa;
char portstr[8];
getaddrinfo(0, 0, &(struct addrinfo){ .ai_flags = AI_PASSIVE, .ai_family = AF_INET }, &ai);
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
bind(s, ai->ai_addr, ai_addrlen);
freeaddrinfo(ai);
getsockname(s, (void *)&sa, &(socklen_t){sizeof sa});
port = ntohs(sa.sin_port);
/* Here, do something to pass the port (assigned by kernel) back to the caller. */
listen(s, 1);
c = accept(s, &sa, &(socklen_t){sizeof sa});
close(s);
getaddrinfo("dbserver", "dbport", 0, &ai);
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
connect(s, ai->ai_addr, ai->ai_addrlen);
freeaddrinfo(ai);此时,您有两个套接字,s连接到数据库服务器,c连接到程序的另一个线程中的数据库客户端。无论您从其中一个读到什么,都应该写到另一个;使用poll来检测哪一个可以读或写。
在上面的设置代码中,除了在accept和connect调用之外,取消应该被阻止,在这种情况下,您需要适当的清理处理程序来关闭套接字并在取消发生时调用freeaddrinfo。将您正在使用的数据从getaddrinfo复制到局部变量可能是有意义的,这样您就可以在阻塞调用之前进行freeaddrinfo,而不必担心通过取消清理处理程序这样做。
https://stackoverflow.com/questions/16280418
复制相似问题